Components
Interactive UI elements — inputs, display, chat, feedback, and content rendering
Button
- Primary.filled() — opaque label/systemBackground inverted
- Primary Tinted.prominentGlass() — full refraction, tinted
- Secondary.glass() — translucent with label foreground
- Icon.glass() + .systemGray5 tint
- isInteractivetrue — adds scale/bounce/shimmer on press
- Primary.filled() — opaque label/systemBackground inverted
- Primary Tinted.filled() — opaque tint background
- Secondary.filled() + .secondarySystemFill
- Icon.filled() + .secondarySystemFill
- HighlightScale animation (0.96×) with spring timing
- Primary.btn--primary — opaque label bg, inverts in dark
- Primary Tinted.btn--primaryTinted — tint bg, onLight text
- Secondary.btn--secondary — systemFill bg, label text
- Icon.btn--icon — systemFill bg, fixed square
- Interaction:hover glow, :active scale(0.96)
- Primary/Main CTA — sign up, confirm, continue. One per screen.
- Primary Tinted/Brand-colored CTA — checkout, key actions where tint reinforces brand.
- Secondary/Supporting actions — settings, filters, edit. Lower visual weight than primary.
- Tertiary/Minimal actions — "learn more", "skip", cancel. Text-only, no fill.
- Destructive/Delete, remove, sign out. Red fill signals danger. Pair with confirmation dialog.
- Icon/Toolbar actions, compact controls. Square shape, single icon, no label.
- iOS Button.Traits/State struct controlling content (title, subtitle, image) and style. Assign via
button.traits = .primary(title: ...) - iOS Factory methods/
.primary().primaryTinted().secondary().tertiary().destructive().icon() - iOS ActionKit/Async actions with automatic loading state. Gracetime prevents spinner flash on fast operations.
- iOS ContentView/StatefulView subclass — constraint swapping animates layout changes (title, image, loading)
- iOS .shakeOnError/Option flag — horizontal shake animation when async action completes with error
- Web CSS classes/
.btn .btn--primary .btn--lg— compose style + size. All CSS custom properties from this design language. - Web Markup/
<button class="btn btn--primary btn--lg"><span class="btn-label">...</span><span class="btn-spinner"></span></button> - Web Loading/Add
.btn--loadingclass. Spinner animates via CSS keyframes. Implement gracetime delay in JS. - Web Disabled/HTML
disabledattribute. Opacity 38%, no pointer events, no transform. - Web Accessibility/Use
<button>not<div>. Addaria-busy="true"during loading,aria-disabledfor disabled state. - Android Compose Button/
Button(onClick, colors = ButtonDefaults.buttonColors(containerColor = tint, contentColor = onLight)). Map each style variant to custombuttonColors(). - Android Ripple/Native ripple via
indication = rememberRipple(). No overlay simulation needed — Android provides this by default. - Android Tap target/Minimum touch target 48dp (Material guideline, 4dp larger than iOS 44pt). Use
Modifier.minimumInteractiveComponentSize().
Input
- Filled/Default input style — forms, settings, profile editing. systemFill background on any surface.
- Outline/URL inputs, code fields, contexts where fill blends too much with background.
- Search (capsule)/Global search bars, filter inputs — pairs with capsule buttons visually.
- Mono/API keys, URLs, code snippets, technical identifiers — IBM Plex Mono for clarity.
- Body font/All text inputs use Figtree. iOS 17pt Web 18px. Placeholder at tertiaryLabel.
- Web Focus ring:
border-color: var(--tint); box-shadow: 0 0 0 3px var(--selection)on focus. Never remove focus indicator for keyboard users. - Web Always set
font-family: inheritandfont-size: inheriton inputs. Browsers default to system font otherwise. - Web Use
font-size: 16pxminimum on mobile web to prevent iOS Safari auto-zoom on focus. - Web
autocomplete,inputmode,enterkeyhintattributes improve mobile keyboard experience. - iOS Uses UITextField with custom appearance. Focus managed via becomeFirstResponder/resignFirstResponder.
- Android Use
TextFieldorOutlinedTextFieldin Compose. Map focus ring tofocusedBorderColor, fill tocontainerColorviaTextFieldDefaults.colors(). - Android Software keyboard: use
WindowInsetsCompat.ime()to handle keyboard inset. Pair withBringIntoViewRequesterso focused inputs scroll into view automatically.
Switch
- .switch/Binary on/off settings that take effect immediately — no submit button needed.
- vs. checkbox/Checkboxes are for selection in forms (submitted together). Switches are for instant toggles.
- label pairing/Always pair with a text label. The switch alone doesn't communicate what it controls.
- iOS
Toggle(isOn: $value)with.tint(Color.tint). Native UISwitch handles animation and haptics. - Web Uses
<label class="switch"><input type="checkbox"><span class="switch-track"></span></label>. Hidden checkbox, styled track with::afterpseudo-element thumb. - Web
:focus-visiblering on the track for keyboard accessibility. Tab navigable via the hidden input. - Android Use
Switchcomposable. Map tint tocheckedThumbColor+checkedTrackColorviaSwitchDefaults.colors(). - All Spring easing on thumb movement (easeSpring) matches the physical feel of a real toggle.
Dropdown
- .dropdown/Selection from a predefined list of 3–15 options. Fewer than 3 — use radio buttons or a segmented control. More than 15 — use a searchable list.
- .dropdown--compact/Inline selection within list rows or toolbars where vertical space is constrained.
- .dropdown-divider/Separate logical groups within the menu. Don't overuse — one divider per menu maximum.
- vs. action sheet/Dropdowns are for selection (picking a value). Action sheets are for actions (delete, share, copy).
- iOS Maps to
UIMenuattached to aUIButtonvia.menuproperty with.showsMenuAsPrimaryAction = true. System handles presentation, animation, and haptics. - iOS SwiftUI:
Menu { } label: { }orPickerwith.menustyle. Checkmark on selected item is automatic. - Web Custom component — not
<select>. Uses.dropdown--openclass toggle. Close on outside click via document listener. - Web Keyboard: open with Enter/Space, navigate with Arrow keys, select with Enter, close with Escape. Add
role="listbox"androle="option"witharia-selected. - Android Use
DropdownMenucomposable. Map background tocontainerColor, item colors to FORK label tokens. Dismiss handled automatically byonDismissRequest. - All Selected item shows a checkmark. Menu dismisses on selection.
Avatar
- .avatar--sm/Inline mentions, compact lists, message timestamps. 28pt — fits beside caption text.
- .avatar--md/List items, chat headers, comment threads. 36pt — default for most UI contexts.
- .avatar--lg/Chat messages, user cards, profile previews. 48pt — primary identification.
- .avatar--xl/Profile headers, user detail views. 64pt — hero-level display, one per screen.
- .avatar--ai/AI agent identity. Tint background distinguishes agents from users. Use with an icon, not initials.
- .avatar-group/Shared spaces, group chats, collaborators. Show up to 4 with overflow count.
- iOS Use
AsyncImagewith.clipShape(.circle). Placeholder shows initials while image loads. - iOS Status indicator via overlay with
.alignmentGuide(.bottom, .trailing). Ring matches parent background. - Web
<div class="avatar avatar--lg">JS</div>for initials. Nest<img>for photos.object-fit: coverhandles non-square images. - Web Group overlap uses negative margin +
box-shadowring (not border, to avoid doubling). - Android Use
AsyncImage(Coil) withModifier.clip(CircleShape). Placeholder composable shows--systemFillmapped color while loading. - All AI avatars always use
--tint, which updates when the user swaps tint presets.
Badge
- .badge/Unread count on tabs, nav items, avatar. Red (badge token) = requires attention.
- .badge--tint/Brand-colored notification — new feature, promotion, non-critical update. Uses tint color.
- .badge--dot/Binary status indicator — new content, online presence. No count, just presence.
- .badge--success/Positive status — connected, verified, complete.
- iOS Use
.badge(_ count:)modifier on TabView items. Custom badge via overlay anchor. - Web Position with
position: absolute; top: -4px; right: -4px;on parent withposition: relative. - Android Use
Badgecomposable from Material 3 withBadgedBox. Override colors with FORK tokens viaBadgeDefaults.containerColor. - All Cap count at 99+ to prevent layout overflow. Animate count changes with spring timing.
Tag
UINavigationBar large title on iOS and TopAppBar on Android. On Web use clamp() for fluid sizing. The New tag marks recently added tokens.
- .tag/Inline categorical label — platform, status, version, feature flag. Not interactive by default.
- Platform variants/Flag platform-specific behavior in docs, usage lists, and spec tables. Color encodes the platform at a glance.
- .tag--tint/Brand moments — new features, highlighted items, active states.
- .tag--fill / .tag--outline/Neutral labels — categories, draft status, metadata. Low visual weight.
- vs. Badge/Tags are inline categorical labels. Badges are notification dots / counters positioned on top of another element.
- iOS Use
UILabelwithbackgroundColor+textAlignment: .center+ content insets. Or Compose-style:Textwith padded backgroundShape. - Android Use Compose
Surface(shape = RoundedCornerShape(6.dp), color = systemBlue) { Text(...) }. Map color variants to FORK token values — not Material 3 chip colors. - Web
<span class="tag tag--ios">iOS</span>. Tags are inline elements — they flow with text. No tap target needed unless interactive. - All Tags are not interactive by default. If a tag is tappable (e.g., filter chip), wrap in a
<button>and ensure 44×44pt tap target.
Tabs
- .segmented/Filter or view mode switching within the same content area. 2–4 options max. Compact, inline.
- .tab-bar/Page-level navigation between distinct content sections. Scrollable on mobile. Visible section count can exceed 4.
- vs. dropdown/Use tabs when options are few and should be always visible. Use dropdown when options exceed ~6 or are secondary.
- iOS Segmented:
Picker(.segmented). Tab bar:TabViewwith custom styling or.tabViewStyle(.page). - Web Segmented:
<div class="segmented"><button class="segmented-item active">...</button>...</div>. Toggle.activevia JS. - Web Tab bar scrolls horizontally on narrow viewports via
overflow-x: auto. Hide scrollbar for clean appearance. - Web Use
role="tablist"androle="tab"witharia-selectedfor accessibility. - Android Tab bar:
TabRowcomposable with custom indicator colors. Segmented control: build a custom composable — Android has no native equivalent.
List
- .list/Grouped vertical rows — settings, contacts, conversations, notifications. Rounded container with separator dividers.
- .list-header/Section label between
.listgroups. Overline style (mono, uppercase). Use for temporal ("Today", "Earlier") or alphabetical grouping. - .list-item--unread/Unread/new state — bold title + tint timestamp. Badge in trailing for count. Combine with
.list-item--topfor chat/notification rows. - .list-item--top/Top-aligned layout — trailing slot becomes column (timestamp top, badge below). Use for rows with multi-line subtitle where meta should align with title.
- .list-item--destructive/Danger actions — delete, leave, remove. Red title, typically no leading element.
- .list-row/Horizontal scroll container — stories, suggested friends, categories. Items are
.list-row-itemwith avatar/icon + label below. - .list-swipe-reveal/Swipe action reveal — trailing actions behind the row. Trailing-most action is the full-swipe target. Max 3 actions.
- Inline actions: buttons in
.list-item-trailing— use existing.btn--sm. For Follow/Unfollow, toggle between.btn--primaryTintedand.btn--secondary. - Subtitle truncation: all subtitles auto-clamp to 2 lines with ellipsis. No modifier needed — it's the default behavior.
- iOS
List { ForEach(...) }with.listStyle(.insetGrouped). Section headers:Section(header:). Swipe via.swipeActions(edge:)— leading for archive, trailing for delete. Use.listRowSeparatorTint(.separator). - iOS Unread:
.fontWeight(.semibold)on title + tint color on timestamp. No dot — badge count and bold title are sufficient signals. - Android
LazyColumnwithListItemcomposables. Section headers: sticky headers viastickyHeader { }. Swipe:SwipeToDismisswith custom background. Separators:Divider. - Web
<div class="list">wrapper +.list-itemrows. Hover/active via CSS. Swipe not native on Web — use touch events or show actions on hover/long-press as fallback. Horizontal scroll viaoverflow-x: auto. - All 44pt minimum row height is non-negotiable. Subtitle clamps at 2 lines. Swipe actions: max 3 per side, 80pt each, trailing-most is the full-swipe target. Horizontal list must show peek of next item to signal scrollability.
Card
- .card/Static content container — wraps demos, forms, grouped content. secondarySystemGroupedBackground with border.
- .card--interactive/Tappable card — app previews, dashboard widgets, content links. Adds hover lift and press scale.
- .card-header/Icon + title + description row. Separated from body by separator border.
- .card-footer/Metadata row — timestamps, counts, secondary info. Mono overline style.
- .card-icon/44pt rounded square. Default tint background, override with inline style for custom colors.
- iOS Interactive card:
Button { CardContent() }.buttonStyle(.scale)with custom press animation (0.98× scale, spring timing). - iOS Card icon:
RoundedRectangle(cornerRadius: 10, style: .continuous)with icon centered. - Web
<div class="card card--interactive">for clickable cards. Wrap in<a>for navigation. - Web Grid layout:
.card-gridgives 2-column layout, collapses to 1 column at 720px. - Android
Cardcomposable withCardDefaults.cardColors()andCardDefaults.cardElevation(). Map shadow tokens to elevation dp values.
Modal
- .modal--sm/Confirmations, simple prompts, delete warnings. Focused, one decision. Pair with destructive button for danger actions.
- .modal--md/Forms, settings panels, sharing dialogs. Room for input fields and descriptions.
- .modal--lg/Complex content — preview, multi-step flows, rich editing. Use sparingly — consider a full page instead.
- backdrop blur/8px blur maintains context while focusing attention. Honest material — you can still see what's behind.
- iOS Use
.sheet()for medium/large,.alert()or.confirmationDialog()for small confirmations. - iOS On iOS 26+, sheets use glass material automatically. Match with
.presentationBackground(.glass). - Web Use
<dialog>element orposition: fixedwith backdrop. Trap focus inside modal. Close on Escape key. - Web Add
aria-modal="true",role="dialog",aria-labelledbypointing to modal title. - Android
ModalBottomSheetin Compose Material 3. Map radius tosheetShape, background tocontainerColor. Handle back gesture withBackHandler. - All Dismiss on backdrop tap/click. Trap focus (Tab cycle stays inside). Restore focus to trigger on close.
Empty State
- .empty-state/No data to show — empty inbox, no search results, first-time user. Replaces blank space with guidance.
- .empty-state-icon/48pt outline icon in tertiaryLabel. Illustrative, not decorative — the icon should hint at what belongs here.
- .empty-state-desc/Explain why it's empty AND what the user can do. Max 320pt width for comfortable reading.
- action button/Optional CTA to resolve the empty state. Use primaryTinted for new actions, tertiary for navigation.
- iOS
ContentUnavailableView(iOS 17+) with.label,.description, and.actions. Matches system pattern. - Web
<div class="empty-state">with SVG icon, heading, paragraph, and optional button. Centered in parent. - All Keep the message encouraging, not apologetic. "No messages yet" > "Sorry, nothing here". Guide toward action.
Skeleton
- .skeleton/Loading placeholder that mirrors the shape of incoming content. Better than a spinner for predictable layouts.
- .skeleton--text/Text line placeholders. Stack 2–4 lines. Last line at 60% width creates a natural paragraph end.
- .skeleton--circle/Avatar placeholder. Match the size of the avatar it replaces (28–64pt).
- .skeleton--rect/Image, card icon, or media placeholder. Set width and height to match the target content.
- .skeleton-group/Container that pairs a shape (circle/rect) with text lines. Mimics list item or card layout.
- iOS Use
.redacted(reason: .placeholder)on SwiftUI views for automatic skeleton generation. - iOS Custom shimmer: animate a
LinearGradientmask across the view with spring timing. - Web CSS-only via
::afterpseudo-element withtranslateXanimation. No JS required. - Web Match skeleton dimensions to real content to prevent layout shift when data loads.
- Android Use
Modifier.placeholder()from Accompanist or custom shimmer withInfiniteTransition. Map--skeletontoken color. - All Use
--skeletontoken for background — it's theme-aware and warm-tinted (not pure gray).
Markdown
Getting Started
Building for the long term
Good software is not written — it is rewritten. The best systems evolve through careful iteration, not grand rewrites. Every decision you make today shapes the landscape your future self will navigate.
Core principles
When designing an interface, consider the hierarchy of attention. Users scan before they read, click before they explore, and leave before they understand. Your job is to make each of those moments count.
Design is not just what it looks like and feels like. Design is how it works.
Typography
We use three font families across the system. Each serves a distinct role:
- Besley — display and titles. A transitional serif with warm, confident character.
- Figtree — body and UI text. A geometric sans-serif that's clean and readable at every size.
- IBM Plex Mono — code, data, and labels. Tabular figures, technical precision.
Color tokens
All colors are defined as CSS custom properties and resolve automatically for light and dark themes. The system provides semantic tokens for common use cases like --destructive, --success, and --link.
| Token | Light | Dark | Usage |
|---|---|---|---|
--label | #1A1A18 | #F5F5F2 | Primary text |
--tint | #E86420 | #F07030 | Brand accent, CTAs |
--link | #1270B8 | #1A8DE0 | Inline links |
--destructive | #CE2021 | #E84142 | Delete, errors |
Implementation
The prose class wraps any block of rendered markdown and applies the full typographic scale. Headers use Besley, body uses Figtree, and code blocks use IBM Plex Mono — all automatically.
// Apply the prose styles to any container
<div class="prose">
{{ rendered_markdown }}
</div>
// CSS custom property for tint
document.documentElement.style
.setProperty('--tint', '#E86420');
Inline elements
Inline code like var(--systemFill) uses a subtle tint color. Bold text uses Figtree 600. Italic text uses native italic. Bold italic combines both. Strikethrough marks removed content. Links like color tokens are underlined with a translucent line that solidifies on hover.
Detailed specifications
The h5 heading uses Besley at a smaller size, bridging the gap between h4 and body text. Use it for fine-grained subsections within a longer document.
Revision History
The h6 heading switches to IBM Plex Mono uppercase with letter-spacing — ideal for metadata labels, version tags, and document annotations.
Nested lists
- Design tokens
- Color primitives
- Brand orange
#E86420 - Neutral grays
- Brand orange
- Semantic aliases
- Color primitives
- Typography scale
- Display — Besley
- Body — Figtree
- Mono — IBM Plex Mono
Task list
- Define color tokens in CSS custom properties
- Set up font loading via Google Fonts
- Build typography scale for web
- Add dark-mode media query fallback
- Audit contrast ratios (WCAG AA)
Media
- Define your color tokens in
:root - Load fonts via Google Fonts (single
<link>tag) - Apply
.proseto markdown containers - Toggle themes with
data-theme="dark"
This example demonstrates all markdown elements styled with the design language. The same visual language applies whether content is authored in a CMS, rendered from an API response, or written inline.
- .prose/Wrap any rendered markdown. Applies full typographic scale — h1–h4 (Besley), body (Figtree), code (IBM Plex Mono).
- .prose h1/34px Bold Besley. Page-level title. Only one per document.
- .prose h2/24px Bold Besley. Major sections. Bottom border for visual separation.
- .prose h3/20px Semi Besley. Subsections.
- .prose code/Inline: tint-colored, systemFill bg, rounded. Block (
pre code): neutral, tertiarySystemBackground. - .prose blockquote/3px tint left border, subtle fill, italic. For callouts and quotes.
- .prose h5/16px Semi Besley. Fine-grained subsections within longer documents.
- .prose h6/12px Semibold IBM Plex Mono, uppercase, tracked. Metadata labels, revision tags.
- .prose del/Strikethrough with secondaryLabel color. Marks removed or deprecated content.
- .prose a/Link color with translucent underline. Hover solidifies the underline.
- .prose .task-list/Custom checkboxes with tint fill when checked. No bullets, compact layout.
- .prose img/Full-width, rounded corners, subtle margin. Responsive with max-width: 100%.
- .prose table/Mono uppercase headers, separator borders, compact padding.
- Web Apply
.proseclass to any container with rendered HTML from markdown. Works with any markdown-to-HTML library. - iOS Use
NSAttributedStringwith matching font/color attributes. Map h1→title1 (Besley 28pt Bold), body→Figtree 17pt, code→IBM Plex Mono 14pt. - iOS For chat messages, ChatKit's
MessageAttributedStringBuilderhandles markdown→attributed string conversion with these tokens.
Cursor
A blinking insertion point. Two variants: line (thin bar) for text fields, block (filled rect) for terminals.
- .cursor.cursor--line/Thin 2px bar. Use for typewriter effects, search inputs, and text fields where the cursor sits between characters.
- .cursor.cursor--block/Filled character-width rectangle. Use for terminals and code editors where the cursor sits on a character position.
- Both variants use
--tint— the cursor is a call to action ("type here"). Same color as the terminal prompt symbol. - Both share the
cursor-blinkkeyframes at 1s — one animation across the system. Add.cursor--typingto stop blinking during active input.
- iOS Line cursor: default
UITextFieldtint. Block cursor: customCALayerwithCABasicAnimationon opacity. Use tint color for both. - Android Line cursor:
cursorColor = tinton TextField. Block cursor:Modifier.drawBehindwith toggled rect viarememberInfiniteTransition(). - Web Line:
::beforepseudo-element with 2px width. Block:backgroundfill on en space (\2002). Both useline-height: 1for baseline alignment in flex containers. - All Respects
prefers-reduced-motion— animation stops, cursor stays visible. Blink must be step-based (on/off), never smooth fade.
Dot Indicator
A horizontal row of colored dots. One pattern, multiple uses: tint selection, window controls, status indicators.
- Same pattern, different semantics. The tint picker and traffic lights are both "colored dot rows" — they share visual DNA but serve different purposes.
- .tint-dot/Interactive selection. Active state shows a
--labelborder ring. Use for picking presets (tints, themes, accents). - .window-dot/Status/control indicator. Fixed colors (red/yellow/green) communicate window state. Always show all three.
- The pattern extends to any scenario with 3–5 colored dots in a row: status indicators, step progress, category markers.
- iOS Tint picker: use a horizontal
HStack(spacing: 8)of tappable circles. Active dot:.overlay(Circle().stroke(Color.label, lineWidth: 2)). Traffic lights:NSWindowButtonon macOS, omit on iOS. - Android Tint picker:
Row(horizontalArrangement = Arrangement.spacedBy(8.dp))withBox(Modifier.size(18.dp).clip(CircleShape)). Active:border(2.dp, labelColor, CircleShape). - Web Both use
border-radius: 50%. Tint dots have a 44px invisible::beforefor tap targets. Traffic lights are pure CSS with no interactivity. - All Minimum 44pt tap target for interactive dots. Decorative dots can be smaller but should remain visually balanced (≥12px).
Progress Bar
- .term-progress/Determinate progress — uploads, builds, scans. Shows label + track + percentage. Use when you can calculate exact completion.
- .term-progress-bar/The filled portion. Set
widthvia inline style or JS. Transitions smoothly via--easeFluent. - For indeterminate progress (unknown duration), prefer a spinner or skeleton — not an animated progress bar.
- Tint-colored fill ties the progress to the brand action color. This is intentional — progress is an action in motion.
- iOS Use
ProgressView(.linear)with.tint(Color.tint). Track:tertiarySystemFill. Custom height via.scaleEffect(y: 0.5)orGeometryReader. - Android
LinearProgressIndicator(progress, color = tint, trackColor = tertiarySystemFill). SetModifier.height(4.dp).clip(RoundedCornerShape(2.dp)). - Web CSS
transition: width 0.4s var(--easeFluent)handles smooth updates. Updatestyle.widthvia JS. - All Always show a percentage label — users need to know "how much is left." The label uses mono font for fixed-width digits that don't jitter as numbers change.
Window
A framed container with optional chrome. The base surface for terminals, code previews, embedded browsers, media players — anything that needs windowed containment.
Design tokens
FORK uses warm neutrals instead of pure gray. The base label color is #1A1A18, not #000000.
- .window/The framed container. Provides background, border, radius, shadow, and overflow:hidden. Content-agnostic — put anything inside.
- .window-titlebar/Optional chrome. Contains traffic lights + title or tabs. Omit for chromeless embedded content.
- .window-title/Centered mono label. Use for single-content windows (terminal, file preview, media).
- .window-tabs/Replace title with tabs for multi-view windows. Active tab gets
systemFillbackground. - Chromeless variant — omit
.window-titlebarentirely. The frame still provides elevation and containment without window controls. - Content is free. Terminal puts
.term-surfaceinside. Markdown preview puts.proseinside. Code editor puts syntax-highlighted output inside. The window doesn't care.
- iOS On macOS Catalyst: traffic lights map to
NSWindowButton. On iOS: omit traffic lights, use system navigation chrome (back button, close X). The title bar becomes a plain header. - Android Title bar maps to a custom
TopAppBarwithcontainerColor = tertiarySystemBackground. Traffic lights are decorative only — use system back/close affordances for real interaction. - Web
.windowusesoverflow: hiddento clip child content to the rounded frame. Title is auto-centered via flex + margin offset. Traffic lights are purely visual unless building an Electron/PWA. - All The window follows the system theme — no forced dark mode. Traffic lights always show all three dots. Partial sets (e.g., close only) look broken and signal non-standard behavior.