The parts behind bykc.pro

Design system.

A living library of this site’s components, materials, and small details. The examples here use the same code as the portfolio.

In use on this siteButtons, small controls, and the material they share.

Components / Actions

Button

Usage & properties

Frosted glass, a reflective rim, and a small inset that catches the light. The primary carries the lens; the secondary can stay quiet.

Live preview
Preview surface

Hover, press, or use your keyboard.

Ready to try.
Astro
<Button>View case study</Button>

Variants & sizes

One component. Three ways to use the lens.
Glass button variants and sizes
EmphasisSmall40px min.Medium44px min.Large48px min.
PrimaryLens by default
SecondaryPlain by default
Secondary + lensOptional detail

For quieter actions

Ghost and text links keep the same component API with less visual weight.

After dark

Saturated teal for the primary, smoky glass for the secondary. The lens keeps its own dark ink.

States & loading

Feedback without changing the layout.

Default

Hover

Pressed

Focus

Disabled

Loading

A single reflection

Hover or keyboard focus sends one soft pass across the lens. Pressing gathers light at its lower edge. Nothing shimmers while idle.

A centred loading ring

The ring takes the arrow’s place. A plain button holds its width and centres the ring over the label. Pending actions cannot be triggered twice.

Less motion, same feedback

Reduced motion keeps a visible, still ring and static illumination. Keyboard focus always has a separate outline.

Usage

The shared Astro component.
---
import Button from '../components/Button.astro';
---

<Button href="/work/yohoho">Learn more</Button>
<Button variant="secondary">Download</Button>
<Button variant="secondary" inset>Download</Button>
<Button loading>Save changes</Button>

<section data-glass-surface="dark">
  <Button>Continue</Button>
  <Button variant="secondary">Go back</Button>
</section>
Button properties
PropertyValuesDefaultPurpose
variantprimary | secondary | ghost | linkprimarySets visual emphasis.
sizesm | md | lgmd40, 44, or 48px minimum height for glass buttons.
insetbooleanPrimary: trueAdds the recessed lens; secondary defaults to plain.
iconbooleanMatches insetShows an arrow. Use with inset={false} for a bare arrow.
loadingbooleanfalseShows the ring, marks the action busy, and blocks activation.
disabledbooleanfalseDisables the button or removes a link’s destination.
surfacelight | darkInheritedOverrides the surrounding glass surface.
hrefstringNoneRenders a link for navigation.
typebutton | submitbuttonUse submit for a form action.

Use a link for navigation and a button for an action. Keep labels specific. An inset is part of the same action, never a separate click target. Add an accessible label when visible text does not describe the action.

See it in use Work ↗

Components / Labels

Status chips

A fact attached to the work.

Use a chip for a release stage or a type of work. It has no click action. The dark tint keeps text readable over artwork.

On the page

Public beta Project Artifact

Over artwork

Public beta Artifact
---
import StatusChip from '../components/StatusChip.astro';
---

<StatusChip tone="dark">Public beta</StatusChip>
<StatusChip size="sm">Artifact</StatusChip>

tone: light or dark. size: sm for metadata, md for status. A label stays out of the tab order. Its light still responds to the mouse, but the chip does not lift or press.

See it in use Curated Work ↗

Components / Highlights

Highlights

A small announcement with a next step.

A teal face, a pulsing dot, and an arrow. The shipped highlight opens two choices. Click it again, click outside, or press Escape to close them.

The same highlight used above the homepage title.

Open and closed

The trigger reports its expanded state. Closed choices cannot receive focus. Escape returns focus to the trigger.

Motion with a purpose

The dot fades over 2.4 seconds. The choices enter in 250ms. Reduced motion keeps the dot and the content still.

---
import ShippedChip from '../components/hero/ShippedChip.astro';
---

<ShippedChip id="release-highlight" placement="below" />

Give each instance a unique id. The default placement opens beside the highlight, then below it on narrow screens. Use placement="below" inside a smaller container.

Components / Prompts

Prompts

An invitation to interact.

An icon gives the interaction a cue; the text says what to do. The voice bars stay still until the hero speaks. On the site, their movement comes from the speech animation.

Click the prompt to show a sample caption.

Disabled
import Prompt from '../components/Prompt';

<Prompt onClick={onHello}>
  Psst. Pick an object. I’ll explain.
</Prompt>

A React button used by the hero. It accepts button attributes, an action, and a text label. Text wraps on small screens; the icon keeps its size. This sample reveals a caption without starting audio.

Components / Selection

Filters & sorting

Narrow a list. Change its order.

One filter is selected at a time. Counts describe each category before filtering. The sort control reverses the date order and keeps the current selection.

17 entries · Oldest first

  1. It started on Windows, in about two hoursproduct
  2. Whisper was the obvious choice, and it lostbuild
  3. The version that typed "wordsmashedtogether"build
  4. One portable core, thin shells on topbuild
  5. An interface for the CLIdesign
  6. The caret macOS could not finddesign
  7. macOS trusts your terminal, not your toolbuild
  8. The ticker bar that did not survivedesign
  9. Two directions I killeddesign
  10. Why it is a dot matrixdesign
  11. Cancelling something that cannot be cancelledbuild
  12. A window that must never take focusbuild
  13. A second model, only if you want onebuild
  14. The ship, and why it is waterdesign
  15. Free, because anyone could build itproduct
  16. The real limit is social, not technicalthought
  17. Eight days, because I already knew what I wantedthought

Entries from Yohoho’s case study. Scroll the preview to see the rest.

Empty category

Selection is explicit

The active filter has a teal face, a heavier label, and aria-pressed="true". These controls filter a list. Tabs that switch panels are a different component.

Room for touch

Controls are at least 44px tall. In this preview, filters wrap and sorting gets its own space on narrow screens. Keyboard users can reach each control and press Enter or Space.

---
import FilterChip from '../components/FilterChip.astro';
import SortControl from '../components/SortControl.astro';
---

<FilterChip selected count={17}>All</FilterChip>
<FilterChip count={6}>Design</FilterChip>
<FilterChip count={0} disabled>Unfiled</FilterChip>
<SortControl direction="asc" />

The surrounding list owns filtering and sorting. Update aria-pressed on filters, then the sort control’s data-sort, label, and accessible name together. A zero count disables an unavailable category.

See it in use Yohoho’s record ↗

Foundations

Colour

Click a swatch to copy its value.

Values come from the site’s colour tokens.

Typography

Two families. No extras.
BitterHeadings / 350

Design. Build.
Ship. Repeat.

A light slab with room to breathe. Tracking −0.025em; teal punctuation at weight 550.

Hanken GroteskBody / 300

A little curiosity.
A lot of making.

Open, easy body text at 1.6 line height. The same family handles labels, controls, and code.

Primitives / Material

Frosted glass

The surface beneath the components.

Blur softens what sits behind the glass. Tint and grain give the face its texture. A moving reflection and a thin rim make it catch the light. Turn off a layer to see what it contributes.

Live material
Shared material

Frosted glass.

Move the light. Watch the edge.

Move your mouse across this surface.

Inside the material
Light X
20%
Light Y
0%
Angle
−45°
Blur
7.5px

Mouse position controls the face reflection and the rim together.

How the mouse becomes light

Each glass surface measures the mouse relative to its own centre. That direction rotates the rim’s conic gradient. The same position moves a radial reflection across the face. The component itself does not tilt.

One shared pointer listener batches the updates into an animation frame. It measures every surface first, then writes the lighting values. There is no idle rotation or simulated spring.

Where the frost lives

The face sits on ::before, with a 7.5px backdrop blur and 1.35× saturation. The grain repeats every 102.4px. The rim sits on ::after, masked to leave its centre clear. Text stays above both.

Nested glass darkens its face by 10% at the second level and 18% from the third. The label and bright rim keep their colour.

<!-- BaseLayout supplies the shared lighting listener. -->
<span class="glass glass-label">Public beta</span>
<button type="button" class="glass glass-primary glass-control">
  Continue
</button>

/* Component CSS owns its shape and spacing. */
.my-surface { border-radius: 18px; padding: 24px; }

/* The listener writes these values on each surface. */
--glass-light-x: 20%;
--glass-light-y: 0%;
--glass-angle: -45deg;

/* Rim direction from a mouse point relative to the centre: */
angle = atan2(mouseY - centreY, mouseX - centreX) * 180 / PI + 90;

The face position is clamped to −30–130% horizontally and −80–180% vertically, so nearby mouse movement can still light an edge. Add data-glass-light="manual" only when a demonstration owns those values itself.

Interaction & motion

Shared glass interaction and motion rules
TriggerVisible responseTiming
Mouse movementFace light and rim follow position. Labels remain still.At most once per animation frame
Hover a controlLift 1px, brighten the rim, deepen the shadow.150ms · ease
Press a controlScale to 97.5%. Travel 1px, or 0.4px for primary glass.75ms · ease
Activate a controlBrightness fades from 1.12 to 1 on the face layer.300ms · ease-out
Enter a button lensOne reflection pass across the inset.260ms · ease-out
Keyboard focusA separate outline identifies the active control.Immediate

Touch

Touch does not steer the light. The surface starts with its upper-left reflection. Tapping still gives press and release feedback. The manual controls above let you inspect lighting without a mouse.

Reduced motion

Light stays at X 20%, Y 0%, and −45°. Lift, scale, release flash, and pulsing dots stop. Selection, focus outlines, and open or closed states remain visible.

The material comes from glass.css; the lighting geometry comes from glass-light.ts. These examples use both.