diff --git a/Digital Rust Syntax Highlighting Specification.md b/Digital Rust Syntax Highlighting Specification.md index 14b3097..8d76be8 100644 --- a/Digital Rust Syntax Highlighting Specification.md +++ b/Digital Rust Syntax Highlighting Specification.md @@ -887,6 +887,11 @@ Some editors or language grammars provide limited token granularity. When fine-g ## Version History +- **v1.2.1**: UI specification accessibility and token fixes + - Primary button hover fill changed to Firewall Breach: Ghost Pixel on raw Amber Alert failed WCAG AA (3.83:1); Firewall Breach passes at 4.66:1 + - Semantic action/feedback/border-colour token layer added to `tokens.css` (`--dr-accent`, `--dr-accent-hover`, `--dr-success`, `--dr-warning`, `--dr-danger`, `--dr-danger-text`, `--dr-info`, `--dr-border-color*`); component code now references semantic tokens, not raw palette values + - Ambient texture (scanline/noise overlay) implementation documented; overlays now suppress under `prefers-reduced-motion` + - Loading/skeleton state specified; unused decorative tokens removed - **v1.2.0**: UI specification added - New sibling document: the Digital Rust UI Specification, covering surfaces, elevation, interaction physics, and component definitions - This document retains the palette, syntax tokens, editor chrome, terminal configuration, and colour-usage summaries diff --git a/Digital Rust UI Specification.md b/Digital Rust UI Specification.md index eb245a1..e03072c 100644 --- a/Digital Rust UI Specification.md +++ b/Digital Rust UI Specification.md @@ -1,6 +1,6 @@ # Digital Rust UI Specification -Part of Digital Rust Specification v1.2.0 +Part of Digital Rust Specification v1.2.1 This document is the canonical specification for Digital Rust as a **user interface theme**: surfaces, elevation, interaction physics, and component definitions. It is @@ -12,6 +12,24 @@ references colours by name only. A conforming UI implementation uses the named colours from the syntax specification, and follows the component, elevation, and interaction rules below. +**Semantic tokens.** Component implementations reference the semantic `--dr-*` +tokens defined in `tokens.css`, never raw palette values. On top of the +surface and text tokens, the semantic layer is: + +| Token | Colour | Role | +| --- | --- | --- | +| `--dr-accent` | Digital Rust | Primary brand accent: primary buttons, checked states, active links | +| `--dr-accent-hover` | Firewall Breach | Primary hover fill; Ghost Pixel on it passes AA (4.66:1), raw Amber Alert does not | +| `--dr-success` | Memory Leek | Success borders, text, fills | +| `--dr-warning` | Stack Overglow | Warning borders, text, fills; the focus ring | +| `--dr-danger` | Amber Alert | Error borders and fills | +| `--dr-danger-text` | Amber Alert Text | AA-compliant error text on base/elevated surfaces | +| `--dr-info` | Dead Thread | Informational accents | +| `--dr-border-color` | Dark Packet | The 2px wall on interactive elements | +| `--dr-border-color-well` | Bad Sector | Recessed input wells | +| `--dr-border-color-hover` | Digital Rust | Ghost-control hover, focused wells | +| `--dr-border-color-invalid` | Amber Alert | Invalid controls | + ## Contents - [Design Principles](#design-principles) @@ -89,6 +107,45 @@ applies to all *ambient* surfaces: page backgrounds, cards, panels, modals. Interactive text surfaces — inputs, text areas, code editors — are exempt. Texture inside a text field fights the text; legibility outranks atmosphere. +**Implementation.** The texture is two full-page fixed overlays, stacked above +content with `pointer-events: none`: a faint noise layer (SVG `feTurbulence` +tile at ~2% opacity) and a scanline layer (4px repeating band in +`--dr-scanline` directly under the noise). Content stays below both. + +```css +body { + position: relative; + z-index: var(--dr-z-content); +} +body::before { /* noise */ + content: ""; + position: fixed; + inset: 0; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); + opacity: 0.02; + pointer-events: none; + z-index: var(--dr-z-noise); +} +body::after { /* scanlines */ + content: ""; + position: fixed; + inset: 0; + background: repeating-linear-gradient(0deg, + transparent, + transparent 2px, + var(--dr-scanline) 2px, + var(--dr-scanline) 4px); + pointer-events: none; + z-index: calc(var(--dr-z-noise) - 1); +} +@media (prefers-reduced-motion: reduce) { + body::before, body::after { content: none; } +} +``` + +The overlays suppress entirely under `prefers-reduced-motion: reduce` and never +render inside wells. + ## Elevation All structural shadows are offset solids in Bad Sector (`--dr-surface-highlight`) @@ -105,7 +162,6 @@ with zero blur. Light is always top-left; shadows fall down-right. | `--dr-shadow-glow-md` | `0 0 20px rgba(175, 46, 26, 0.4)` | Hero-level emphasis only | | `--dr-shadow-inner-highlight` | `inset 0 1px 0 rgba(255, 255, 255, 0.1)` | Top-edge bevel on raised/interactive elements | | `--dr-shadow-inner-lowlight` | `inset 0 -1px 0 rgba(17, 4, 2, 0.6)` | Bottom-edge shade for wells and thumbs | -| `--dr-shadow-accent` | `5px 5px 0 0` Digital Rust | Brand-level emphasis (rare) | **The No Blur Rule.** Blur radius is zero everywhere except the glow shadows, which simulate radiated heat, not material depth. @@ -158,19 +214,47 @@ checkboxes, chips, tabs, and any element that behaves like a switch. ## Typography in UI -UI chrome speaks in Rajdhani, all-caps, tracked; content speaks in Zilla -Slab; values and readouts speak in IBM Plex Mono. These roles come from the -design system and are restated here only as they apply to components: - -- **Button and tab labels**: Rajdhani 600–700, uppercase, 0.05em tracking, - verb + object ("Save changes", not "OK"). -- **Badges and tags**: Rajdhani 700, uppercase, 0.08em tracking, 0.75rem. -- **Panel and card titles**: Rajdhani 700, uppercase, 0.05em tracking, - Fatal Error colour (the One Heading Colour Rule). -- **Inputs and text areas**: body font (Zilla Slab), 1rem, no uppercase. - Placeholder text is muted and italic. -- **Readouts, status bars, hex values, version stamps**: IBM Plex Mono - 0.875rem or smaller, tabular numerals. +Digital Rust pairs three typographic roles. The guidance below describes each +role **generically first**; the named faces are examples of the role, not the +only acceptable choices. + +- **Display, headings, and labels** — a condensed, technical, near-geometric + sans with a slightly mechanical, engineered feel. Set all-caps with wide + tracking for short strings (titles, labels, badges); never all-caps at body + size. Example pairing: Rajdhani, with Saira SemiCondensed and Trebuchet MS + as fallbacks. This is the "embossed server panel" voice. +- **Body and content** — a warm slab serif (or a humanist serif with sturdy, + bracketed terminals) that stays comfortable over long sessions. Sentence case + at body size. Example: Zilla Slab, falling back to Bitter, Source Serif 4, + or Georgia. +- **Monospace readouts** — a monospace with tabular numerals for any value + where character alignment matters: status bars, hex values, version stamps, + code. Example: IBM Plex Mono, with Space Mono, Fira Code, Cascadia Code, or + the platform mono as fallbacks. + +These are **general recommendations, not rules that cannot be diverged from**. +Appropriateness to the content and the audience is the most important +criterion: pick faces that read clearly at the required sizes and fit the +product's voice, and treat the guidance above as a tested starting point +rather than a constraint. Where a chosen face changes the character of the +theme, favour warmth and mechanical honesty over decoration. + +Applied to components, the three roles surface as: + +- **Button and tab labels**: heading/label sans (e.g. Rajdhani) at 600–700, + uppercase, 0.05em tracking, verb + object ("Save changes", not "OK"). +- **Badges and tags**: heading/label sans (e.g. Rajdhani) at 700, uppercase, + 0.08em tracking, 0.75rem. +- **Panel and card titles**: heading/label sans (e.g. Rajdhani) at 700, + uppercase, 0.05em tracking, Fatal Error colour (the One Heading Colour Rule). +- **Inputs and text areas**: body serif/sans (e.g. Zilla Slab), 1rem, no + uppercase. Placeholder text is muted and italic. +- **Readouts, status bars, hex values, version stamps**: monospace + (e.g. IBM Plex Mono), 0.875rem or smaller, tabular numerals. + +Across all roles, weights, tracking, and case matter more than the exact face; + swapping in an equivalent category with matching metrics keeps the system + intact. ## Components @@ -186,7 +270,7 @@ light-catching bevel, physical travel. | Padding | `0.5rem 1.5rem` (`--dr-space-sm --dr-space-lg`) | | Border | `2px solid` (colour per variant) | | Radius | `4px` (`--dr-radius`) | -| Font | Rajdhani 600, uppercase, `0.05em` tracking, `--dr-text-sm` | +| Font | heading/label sans (e.g. Rajdhani) at 600, uppercase, `0.05em` tracking, `--dr-text-sm` | | Shadow | resting offset + inner highlight | | Cursor | `pointer` | | Interaction | full press cycle at 150ms | @@ -196,7 +280,7 @@ light-catching bevel, physical travel. | Variant | Rest | Hover | Active | | --- | --- | --- | --- | | **Default / Ghost** | Transparent bg, Phosphor Smoke text, Dark Packet border, `offset-sm` | Core Meltdown fill, Ghost Pixel text, Digital Rust border, `hover-md`, lifts | `offset` collapses, presses in | -| **Primary** | Digital Rust bg, Ghost Pixel text, Digital Rust border, `offset-md` | Amber Alert bg and border, `hover-md` + `glow-sm`, lifts | `offset` collapses, glow removed, presses in | +| **Primary** | Digital Rust bg, Ghost Pixel text, Digital Rust border, `offset-md` | Firewall Breach bg and border, `hover-md` + `glow-sm`, lifts | `offset` collapses, glow removed, presses in | | **Success** | Memory Leek border and text | Memory Leek bg, Event Horizon text | presses in | | **Warning** | Stack Overglow border and text | Stack Overglow bg, Event Horizon text | presses in | | **Error** | Amber Alert border, Amber Alert Text (`#cb6353`) text | Amber Alert bg, Event Horizon text | presses in | @@ -248,8 +332,8 @@ Reference CSS: box-shadow: var(--dr-shadow-offset-md), var(--dr-shadow-inner-highlight); } .dr-btn-primary:hover { - background: var(--amber-alert); - border-color: var(--amber-alert); + background: var(--firewall-breach); + border-color: var(--firewall-breach); transform: var(--dr-press-hover); box-shadow: var(--dr-shadow-hover-md), @@ -273,14 +357,14 @@ Recessed wells. The darkest surface on the page; the user types into the machine | Property | Value | | --- | --- | | Background | Event Horizon (`--dr-surface-base`) | -| Border | `2px solid` Bad Sector (`--dr-surface-highlight`) | +| Border | `2px solid` Bad Sector (`--dr-border-color-well`) | | Radius | `4px` | | Padding | `0.5rem 0.75rem` | | Font | body font, `--dr-text-base`, Ghost Pixel text | | Shadow | none at rest (wells do not rise) | -| Placeholder | muted text (`rgba(239, 213, 209, 0.5)`), italic | -| Hover (unfocused) | border shifts to Dark Packet | -| Focus | border shifts to Digital Rust; 2px Stack Overglow outline at 2px offset | +| Placeholder | muted text (`--dr-text-muted`), italic | +| Hover (unfocused) | border shifts to Dark Packet (`--dr-border-color`) | +| Focus | border shifts to Digital Rust (`--dr-border-color-hover`); 2px Stack Overglow outline at 2px offset | | Disabled | transparent border, muted text, no shadow | | Invalid | Amber Alert border; error message in Amber Alert Text | @@ -314,8 +398,8 @@ Recessed wells. The darkest surface on the page; the user types into the machine - Wells are exempt from noise/scanline texture (see The Recessed Well Rule). - No offset shadows on wells. Depth on inputs is communicated by darkness, not elevation. -- Labels sit above the input in Rajdhani uppercase at `--dr-text-xs`, - Phosphor Smoke. +- Labels sit above the input in the label sans (e.g. Rajdhani), uppercase at + `--dr-text-xs`, Phosphor Smoke. ### Checkboxes @@ -368,7 +452,8 @@ content behind them; they are solid panels bolted over it. ### Modals & Dialogs The highest layer. Core Meltdown bg, 2px Dark Packet border, 4px radius, -`offset-lg` + inner highlight. Title in Fatal Error, Rajdhani 700 uppercase. +`offset-lg` + inner highlight. Title in Fatal Error, label sans (e.g. Rajdhani) +at 700 uppercase. Backdrop: warm-tinted scrim, `rgba(17, 4, 2, 0.7)` — never pure black, never blurred. The footer action row carries at most one primary button. Modals do not animate in with scale/bounce; a 150ms fade is the only permitted entry motion. @@ -392,14 +477,15 @@ tooltips use `offset-md`. ### Badges & Tags Semantic status chips: small, dense, tracked. 4px radius, -`0.2rem 0.6rem` padding, Rajdhani 700 uppercase, 0.08em tracking, 0.75rem. +`0.2rem 0.6rem` padding, label sans (e.g. Rajdhani) at 700 uppercase, 0.08em +tracking, 0.75rem. | Kind | Text | Background | Border | | --- | --- | --- | --- | -| Add / Success | Memory Leek | 15% Memory Leek | 1px Memory Leek 30% | -| Modified / Info | Dead Thread | 15% Dead Thread | 1px Dead Thread 30% | -| Delete / Error | Amber Alert Text | 10% Amber Alert | 1px Amber Alert 25% | -| Warning | Stack Overglow | 15% Stack Overglow | 1px Stack Overglow 30% | +| Add / Success | Memory Leek | 15% Memory Leek (`--memory-leek-15`) | 1px Memory Leek 30% (`--memory-leek-30`) | +| Modified / Info | Dead Thread | 15% Dead Thread (`--dead-thread-15`) | 1px Dead Thread 30% (`--dead-thread-30`) | +| Delete / Error | Amber Alert Text | 10% Amber Alert (`--amber-alert-10`) | 1px Amber Alert 25% (`--amber-alert-25`) | +| Warning | Stack Overglow | 15% Stack Overglow (`--stack-overglow-15`) | 1px Stack Overglow 30% (`--stack-overglow-30`) | Badges are static indicators: no press cycle, no shadow. Full-pill radius is permitted on badges only (they are rivets, not panels). @@ -431,16 +517,16 @@ vice versa. ### Status Bars -Bricked Board bg, Ghost Pixel text, 2px Dark Packet top border, IBM Plex Mono at -`--dr-text-xs`, `0.25rem 0.5rem` item padding. Items respond to hover with +Bricked Board bg, Ghost Pixel text, 2px Dark Packet top border, monospace +(e.g. IBM Plex Mono) at `--dr-text-xs`, `0.25rem 0.5rem` item padding. Items respond to hover with Phantom Current text only — status bar items do not lift. Semantic items: Memory Leek (success/OK), Stack Overglow (warning), Amber Alert Text (error). ### Tables -Header row: Rajdhani 700 uppercase `--dr-text-xs`, Fatal Error text, 2px Dark -Packet bottom border. Body: Zilla Slab, Ghost Pixel. Row separators: 1px Bad -Sector. Row hover: Bad Sector bg. Zebra striping is forbidden (the scanline +Header row: label sans (e.g. Rajdhani) at 700 uppercase `--dr-text-xs`, Fatal +Error text, 2px Dark Packet bottom border. Body: body serif/sans (e.g. Zilla +Slab), Ghost Pixel. Row separators: 1px Bad Sector. Row hover: Bad Sector bg. Zebra striping is forbidden (the scanline overlay already bands the page; double banding reads as interference). ## States @@ -455,6 +541,16 @@ overlay already bands the page; double banding reads as interference). | Loading / Processing | Stack Overglow with motion (spinner, pulse); under reduced motion, static Stack Overglow text label. | | Invalid / Error | Amber Alert border on the control + Amber Alert Text message. Errors never rely on colour alone: icon or text accompanies. | +### Loading & skeletons + +Busy indicators are readouts, not pressables: Stack Overglow text or spinner, +monospace, never a press cycle. Skeleton placeholders are recessed wells, not +raised panels — Event Horizon fill with a Bad Sector border and a 2px-high +Stack Overglow progress line along the bottom edge when determinate progress +is known; indeterminate work pulses the line at 400ms (`--dr-duration-slow`). +Under `prefers-reduced-motion`, the pulse is removed and the label alone +carries the state. + ## Accessibility - WCAG 2.1 AA contrast is required on all text, as defined in the syntax diff --git a/index.html b/index.html index 66987de..f1ae1fa 100644 --- a/index.html +++ b/index.html @@ -22,7 +22,7 @@

DIGITAL RUST

- Spec v1.2.0 + Spec v1.2.1
Syntax · UI · Terminal
A warm, tech-dystopian color scheme inspired by corrupted hardware and failing systems.
@@ -60,7 +60,7 @@ cat DIGITAL_RUST_SPEC.md | verify --strict
- fingerprint: 0xDR-1.2.0
+ fingerprint: 0xDR-1.2.1
updated: 2025-11-29
priority-order: errors > warnings > semantic > textmate
diff --git a/playground/src/app.css b/playground/src/app.css index 52b4f2d..9ada355 100644 --- a/playground/src/app.css +++ b/playground/src/app.css @@ -28,7 +28,7 @@ --fatal-error: #cf8175; --rouge-signal: #c76c5e; --amber-alert: #bf5747; - --amber-alert-text: #cb6353; + --amber-alert-text: #cb6353; /* WCAG AA compliant text variant on base & elevated */ --firewall-breach: #b74230; --digital-rust: #af2e1a; --kernel-panic: #9d2917; @@ -71,16 +71,34 @@ /* Semantic text */ --dr-text-primary: var(--ghost-pixel); --dr-text-secondary: var(--phosphor-smoke); + --dr-text-muted: rgba(239, 213, 209, 0.5); --dr-text-link: var(--phantom-current); --dr-text-link-hover: var(--fatal-error); --dr-text-link-visited: var(--corrupted-cache); --dr-text-on-accent: var(--ghost-pixel); + /* Semantic action tokens */ + --dr-accent: var(--digital-rust); + --dr-accent-hover: var(--firewall-breach); /* AA-compliant primary hover fill (4.66:1) */ + + /* Semantic feedback tokens */ + --dr-success: var(--memory-leek); + --dr-warning: var(--stack-overglow); + --dr-danger: var(--amber-alert); + --dr-danger-text: var(--amber-alert-text); + --dr-info: var(--dead-thread); + + /* Semantic border colour tokens (widths live in the border tokens) */ + --dr-border-color: var(--dark-packet); + --dr-border-color-well: var(--bad-sector); + --dr-border-color-hover: var(--digital-rust); + --dr-border-color-invalid: var(--amber-alert); + /* Borders */ - --dr-border: 2px solid var(--dark-packet); - --dr-border-thick: 4px solid var(--dark-packet); - --dr-border-subtle: 1px solid var(--bad-sector); - --dr-border-focus: 2px solid var(--stack-overglow); + --dr-border: 2px solid var(--dr-border-color); + --dr-border-thick: 4px solid var(--dr-border-color); /* page/site chrome only */ + --dr-border-subtle: 1px solid var(--dr-border-color-well); + --dr-border-focus: 2px solid var(--dr-warning); /* Shadows — warm, never pure black */ --dr-shadow-offset-sm: 2px 2px 0 0 var(--dr-surface-highlight); @@ -91,7 +109,16 @@ --dr-shadow-glow-sm: 0 0 10px rgba(175, 46, 26, 0.4); --dr-shadow-glow-md: 0 0 20px rgba(175, 46, 26, 0.4); --dr-shadow-inner-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.1); - --dr-shadow-accent: 5px 5px 0 0 var(--digital-rust); + --dr-shadow-inner-lowlight: inset 0 -1px 0 rgba(17, 4, 2, 0.6); + + /* Press cycle */ + --dr-press-rest: translate(0, 0); + --dr-press-hover: translate(-2px, -2px); + --dr-press-active: translate(2px, 2px); + + /* Focus ring */ + --dr-focus-ring: 2px solid var(--dr-warning); + --dr-focus-ring-offset: 2px; /* Radius */ --dr-radius-sm: 2px; @@ -132,7 +159,9 @@ /* Scanline colour */ --dr-scanline: rgba(175, 46, 26, 0.03); - /* Font stacks */ + /* Font stacks — illustrative only, NOT a constraint; any families of + equivalent character (condensed uppercase heading, slab/humanist + serif body, tabular monospace readouts) satisfy the role. */ --dr-font-heading: "Rajdhani", "Saira SemiCondensed", "Trebuchet MS", sans-serif; --dr-font-body: "Zilla Slab", "Bitter", "Source Serif 4", Georgia, serif; --dr-font-mono: "IBM Plex Mono", "Space Mono", ui-monospace, "Fira Code", @@ -150,6 +179,7 @@ body { display: flex; flex-direction: column; position: relative; + z-index: var(--dr-z-content); overflow-x: hidden; line-height: 1.6; } @@ -159,7 +189,7 @@ body::before { content: ""; position: fixed; inset: 0; - background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); opacity: 0.02; pointer-events: none; z-index: var(--dr-z-noise); @@ -200,7 +230,7 @@ body::after { left: 0; z-index: calc(var(--dr-z-noise) + 1); padding: var(--dr-space-sm) var(--dr-space-lg); - background: var(--digital-rust); + background: var(--dr-accent); color: var(--dr-text-on-accent); font-family: var(--dr-font-heading); font-weight: 700; @@ -209,7 +239,8 @@ body::after { text-decoration: none; border: var(--dr-border); border-radius: 0 0 var(--dr-radius) 0; - box-shadow: var(--dr-shadow-offset-sm); + box-shadow: var(--dr-shadow-offset-sm), + var(--dr-shadow-inner-highlight); } .skip-link:focus { @@ -239,7 +270,7 @@ header::after { h1 { margin: 0; - color: var(--fatal-error); + color: var(--fatal-error); /* the One Heading Colour Rule */ font-family: var(--dr-font-heading); font-size: var(--dr-text-xl); font-weight: 700; @@ -261,7 +292,7 @@ h1 { .disclaimer { margin: var(--dr-space-xs) 0 0; - color: var(--copper-trace); + color: var(--dr-text-secondary); font-size: var(--dr-text-sm); font-style: italic; position: relative; @@ -299,36 +330,35 @@ h1 { letter-spacing: 0.05em; } -/* ── Select ── */ +/* ── Select — a switch: full press cycle ── */ select { background-color: var(--dr-surface-elevated); color: var(--dr-text-primary); border: var(--dr-border); + border-color: var(--dr-border-color); padding: var(--dr-space-xs) var(--dr-space-sm); border-radius: var(--dr-radius); font-family: var(--dr-font-mono); font-size: var(--dr-text-sm); cursor: pointer; - box-shadow: var(--dr-shadow-offset-sm); + box-shadow: var(--dr-shadow-offset-sm), + var(--dr-shadow-inner-highlight); transition: border-color var(--dr-transition-fast), box-shadow var(--dr-transition-fast), transform var(--dr-transition-fast); } -select:focus { - outline: none; - border-color: var(--stack-overglow); - box-shadow: var(--dr-shadow-offset-sm), - 0 0 0 1px rgba(212, 167, 89, 0.3); +select:hover:not(:focus-visible) { + border-color: var(--dr-border-color-hover); } -select:hover { - transform: translate(-1px, -1px); - box-shadow: var(--dr-shadow-hover-md); +select:focus-visible { + outline: var(--dr-focus-ring); + outline-offset: var(--dr-focus-ring-offset); } select:active { - transform: translate(1px, 1px); + transform: var(--dr-press-active); box-shadow: var(--dr-shadow-active); } @@ -352,7 +382,7 @@ a:visited { } a:active { - color: var(--digital-rust); + color: var(--dr-accent); } .back-button { @@ -365,7 +395,8 @@ a:active { border: var(--dr-border); border-radius: var(--dr-radius); background: transparent; - box-shadow: var(--dr-shadow-offset-sm); + box-shadow: var(--dr-shadow-offset-sm), + var(--dr-shadow-inner-highlight); transition: transform var(--dr-transition-fast), box-shadow var(--dr-transition-fast), background var(--dr-transition-fast), @@ -375,15 +406,17 @@ a:active { } .back-button:hover { - background: var(--dr-surface-highlight); + background: var(--dr-surface-elevated); + border-color: var(--dr-border-color-hover); color: var(--dr-text-primary); - transform: translate(-2px, -2px); - box-shadow: var(--dr-shadow-hover-md); + transform: var(--dr-press-hover); + box-shadow: var(--dr-shadow-hover-md), + var(--dr-shadow-inner-highlight); text-decoration: none; } .back-button:active { - transform: translate(2px, 2px); + transform: var(--dr-press-active); box-shadow: var(--dr-shadow-active); } @@ -407,7 +440,8 @@ a:active { overflow: hidden; box-shadow: var(--dr-shadow-offset-md), var(--dr-shadow-inner-highlight); - transition: border-color var(--dr-transition-normal); + transition: border-color var(--dr-transition-normal), + box-shadow var(--dr-transition-normal); } .input-panel:hover, @@ -415,12 +449,12 @@ a:active { border-color: var(--daemon-blood); } -/* ── Panel Headers — Bracket Notation ── */ +/* ── Panel Headers — Bracket Notation (Bad Sector title bar) ── */ h2 { margin: 0; padding: var(--dr-space-sm) var(--dr-space-md); - background: var(--dr-surface-highlight); - color: var(--phosphor-smoke); + background: var(--dr-surface-highlight); /* panel headers: Bad Sector */ + color: var(--dr-text-secondary); font-size: var(--dr-text-sm); font-family: var(--dr-font-heading); font-weight: 700; @@ -430,12 +464,12 @@ h2 { } .title-bracket { - color: var(--dark-packet); + color: var(--dr-border-color); opacity: 0.5; font-weight: 400; } -/* ── Textarea (Input) ── */ +/* ── Textarea (Input) — Recessed Well ── */ textarea { flex: 1; background-color: var(--dr-surface-base); @@ -451,13 +485,13 @@ textarea { } textarea::placeholder { - color: var(--copper-trace); + color: var(--dr-text-muted); font-style: italic; } -textarea:focus { - outline: none; - box-shadow: inset 0 0 0 2px rgba(212, 167, 89, 0.2); +textarea:focus-visible { + outline: var(--dr-focus-ring); + outline-offset: var(--dr-focus-ring-offset); } /* ── Code Output ── */ @@ -470,11 +504,11 @@ textarea:focus { font-size: var(--dr-text-sm); line-height: 1.6; scrollbar-width: thin; - scrollbar-color: var(--bad-sector) transparent; + scrollbar-color: var(--dr-border-color) transparent; } .code-output:hover { - scrollbar-color: var(--dark-packet) transparent; + scrollbar-color: var(--dr-border-color) transparent; } .code-output pre { @@ -485,62 +519,65 @@ textarea:focus { background-color: transparent !important; } -/* ── Loading State ── */ +/* ── Loading State — readout, not pressable ── */ .loading { - color: var(--stack-overglow); + color: var(--dr-warning); font-style: italic; } -/* ── Status Bar ── */ +/* ── Status Bar ── + Bricked Board bg (hotter, more urgent panel than Core Meltdown) */ .status { margin-top: var(--dr-space-md); padding: var(--dr-space-sm) var(--dr-space-md); - background: var(--dr-surface-secondary); - color: var(--dr-text-secondary); - border: var(--dr-border); - border-radius: var(--dr-radius); - box-shadow: var(--dr-shadow-offset-sm), - var(--dr-shadow-inner-highlight); + background: var(--bricked-board); + color: var(--dr-text-primary); + border-top: var(--dr-border); + border-left: none; + border-right: none; + border-bottom: none; + border-radius: 0; + box-shadow: none; font-family: var(--dr-font-mono); - font-size: var(--dr-text-sm); + font-size: var(--dr-text-xs); display: flex; align-items: center; gap: var(--dr-space-sm); } #status-indicator { - color: var(--stack-overglow); + color: var(--dr-warning); font-weight: 700; } #status-indicator.ready { - color: var(--memory-leek); + color: var(--dr-success); } .status-separator { - color: var(--dark-packet); + color: var(--dr-border-color); user-select: none; } .status-tagline { - color: var(--copper-trace); + color: var(--dr-text-secondary); font-style: italic; font-family: var(--dr-font-body); } -/* ── Focus Visible ── */ +/* ── Focus Visible — 2px Stack Overglow, 2px offset, in addition to resting shadow ── */ :focus-visible { - outline: 2px solid var(--stack-overglow); - outline-offset: 2px; + outline: var(--dr-focus-ring); + outline-offset: var(--dr-focus-ring-offset); } -/* ── Selection ── */ +/* ── Selection — warm, never pure black, never cold blue ── */ ::selection { - background: var(--digital-rust); + background: var(--dr-accent); color: var(--dr-text-on-accent); } -/* ── Scrollbars ── */ +/* ── Scrollbars — machine rails: Dark Packet rest, Daemon Blood hover, surface-active drag ── */ ::-webkit-scrollbar { width: 8px; height: 8px; @@ -551,21 +588,30 @@ textarea:focus { } ::-webkit-scrollbar-thumb { - background: var(--dark-packet); - border-radius: 4px; + background: var(--dr-border-color); /* Dark Packet at rest */ + border-radius: var(--dr-radius-sm); border: 2px solid var(--dr-surface-secondary); } ::-webkit-scrollbar-thumb:hover { - background: var(--digital-rust); + background: var(--daemon-blood); /* Daemon Blood on hover */ +} + +::-webkit-scrollbar-thumb:active { + background: var(--dr-surface-active); } * { scrollbar-width: thin; - scrollbar-color: var(--dark-packet) transparent; + scrollbar-color: var(--dr-border-color) transparent; +} + +*:hover { + scrollbar-color: var(--daemon-blood) transparent; } -/* ── Glitch Animation ── */ +/* ── Glitch Animation ── + Page chrome signature; tied to user motion preference below. */ @keyframes glitch { 0% { text-shadow: 0 0 24px rgba(175, 46, 26, 0.7), -1px -1px var(--digital-rust), @@ -677,14 +723,28 @@ textarea:focus { } } -/* ── Reduced Motion ── */ +/* ── Reduced Motion — all transforms/transitions/animation suppressed, + texture overlays removed, glitch disabled (spec: state colours remain) ── */ @media (prefers-reduced-motion: reduce) { + h1 { + animation: none !important; + transform: none !important; + filter: none !important; + } + *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; + transition-delay: 0ms !important; scroll-behavior: auto !important; } + + /* Kill the ambient overlays entirely (spec: no texture under reduced motion) */ + body::before, + body::after { + content: none; + } } diff --git a/playground/src/lib/digital-rust-theme.ts b/playground/src/lib/digital-rust-theme.ts index a745a3a..9787773 100644 --- a/playground/src/lib/digital-rust-theme.ts +++ b/playground/src/lib/digital-rust-theme.ts @@ -358,6 +358,7 @@ export const digitalRustTheme = { }, // ─── Diff / Git ─── + // Diff additions: Ghost Pixel text, Memory Leek 15% bg, + marker Memory Leek { name: 'Diff Header (hunk)', scope: ['meta.diff.header', 'meta.diff.range.unified'], @@ -366,24 +367,70 @@ export const digitalRustTheme = { }, }, { - name: 'Diff Inserted', - scope: ['markup.inserted', 'meta.diff.insertion'], + name: 'Diff Commit Hashes', + scope: ['constant.other.hash.diff'], + settings: { + foreground: '#a07e43', + }, + }, + { + name: 'Diff File Paths / Metadata', + scope: [ + 'meta.diff.header.from-file', + 'meta.diff.header.to-file', + 'entity.name.file.diff', + 'meta.diff.index', + ], + settings: { + foreground: '#d4a759', + }, + }, + { + name: 'Diff Inserted (+ marker)', + scope: [ + 'punctuation.definition.inserted.diff', + 'punctuation.definition.plus.diff', + ], settings: { foreground: '#8fa667', + }, + }, + { + name: 'Diff Deleted (- marker)', + scope: [ + 'punctuation.definition.deleted.diff', + 'punctuation.definition.minus.diff', + ], + settings: { + foreground: '#bf5747', + }, + }, + { + name: 'Diff Modified (~ marker)', + scope: ['punctuation.definition.changed.diff', 'punctuation.definition.tilde.diff'], + settings: { + foreground: '#6fa5a0', + }, + }, + { + name: 'Diff Added Lines', + scope: ['markup.inserted', 'meta.diff.line.inserted', 'diff.inserted'], + settings: { + foreground: '#f7eae8', background: '#8fa66726', }, }, { - name: 'Diff Deleted', - scope: ['markup.deleted', 'meta.diff.deletion'], + name: 'Diff Deleted Lines', + scope: ['markup.deleted', 'meta.diff.line.deleted', 'diff.deleted'], settings: { foreground: '#e7c0ba', background: '#bf574726', }, }, { - name: 'Diff Modified', - scope: ['markup.changed'], + name: 'Diff Modified Lines', + scope: ['markup.changed', 'meta.diff.line.changed', 'diff.changed'], settings: { foreground: '#f7eae8', background: '#6fa5a01a', @@ -391,6 +438,18 @@ export const digitalRustTheme = { }, // ─── Shell / CLI ─── + { + name: 'Shell Shebang & Comments', + scope: [ + 'comment.line.number-sign.shell', + 'comment.line.fish', + 'comment.line.double-slash', + ], + settings: { + foreground: '#ac6d4a', + fontStyle: 'italic', + }, + }, { name: 'Shell Commands', scope: [ @@ -468,11 +527,43 @@ export const digitalRustTheme = { 'keyword.control.import.include', 'keyword.control.preprocessor', 'meta.preprocessor', + 'keyword.control.preprocessor.define', + 'keyword.control.preprocessor.if', + 'keyword.control.preprocessor.elif', + 'keyword.control.preprocessor.else', + 'keyword.control.preprocessor.endif', + 'keyword.control.preprocessor.include', + 'keyword.control.preprocessor.import', + 'keyword.control.preprocessor.undef', + 'keyword.control.preprocessor.error', + 'keyword.control.preprocessor.warning', + 'keyword.control.preprocessor.pragma', ], settings: { foreground: '#cf8175', }, }, + { + name: 'Preprocessor Macro Names', + scope: [ + 'entity.name.function.preprocessor', + 'entity.name.constant.preprocessor', + ], + settings: { + foreground: '#d3ae6e', + }, + }, + { + name: 'Preprocessor Include Paths', + scope: [ + 'string.quoted.double.include.c', + 'string.quoted.other.lt-gt.include.c', + 'string.include', + ], + settings: { + foreground: '#8fa667', + }, + }, // ─── String Interpolation Delimiters ─── { @@ -488,13 +579,46 @@ export const digitalRustTheme = { }, // ─── Haskell / ML / Functional ─── + { + name: 'Type Constructors (Haskell/ML)', + scope: [ + 'constant.other.capitalized-type.haskell', + 'entity.name.type.data.haskell', + ], + settings: { + foreground: '#6fa5a0', + }, + }, + { + name: 'Data Constructors (Haskell/ML)', + scope: [ + 'constant.other.capitalized-constructor.haskell', + 'entity.name.function.constructor.haskell', + ], + settings: { + foreground: '#d3ae6e', + }, + }, { name: 'Type Variables (Haskell/ML)', - scope: ['variable.other.generic-type.haskell'], + scope: ['variable.other.generic-type.haskell', 'variable.other.type.haskell'], settings: { foreground: '#6fa5a0', fontStyle: 'italic', }, }, + { + name: 'Functional Arrows (::, =>, ->)', + scope: [ + 'keyword.other.double-colon.haskell', + 'keyword.other.fat-arrow.haskell', + 'keyword.other.arrow.haskell', + 'keyword.operator.type-annotation', + 'keyword.operator.arrow', + ], + settings: { + foreground: '#d7968c', + }, + }, ], }; diff --git a/spec.html b/spec.html index c5ee915..d8be316 100644 --- a/spec.html +++ b/spec.html @@ -2554,6 +2554,15 @@

Version History