lib-theme-graphite: initial import (calm-pro/dense-utility set, 2026-07)
One of four divergent candidates for the operator/console theme family (successor to Skyrise for developer-product screens). Dense/pro type register, keyboard-first focus treatment, tabular numerals, coarse-pointer 44px touch floor, neutral code chips, dark card separation per house sweep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
36
README.md
Normal file
36
README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# lib-theme-graphite
|
||||||
|
|
||||||
|
**Graphite — achromatic precision.** Dieter-Rams calm for operator/console/admin
|
||||||
|
surfaces. Every neutral is true achromatic (chroma 0) — near-white grey field,
|
||||||
|
pure white cards, near-black ink — with ONE electric azure accent spent only on
|
||||||
|
the primary action, links, selection, and focus. Dark mode is true dark grey
|
||||||
|
(never blue-black) with cards stepping visibly lighter. Dense/pro type register,
|
||||||
|
compact 0.5rem radius, product-fast motion, crisp low elevation, tabular
|
||||||
|
numerals wired for tables.
|
||||||
|
|
||||||
|
## Fonts
|
||||||
|
|
||||||
|
Consuming apps must load the fonts at the very top of their entry CSS
|
||||||
|
(`app/app.css`), before anything else — the theme file itself does not
|
||||||
|
`@import` fonts (Crema token-contract rule):
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Font stacks
|
||||||
|
|
||||||
|
| Token | Stack | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `--font-sans` / `--body-family` | `"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif` | body text |
|
||||||
|
| `--font-heading` / `--heading-family` | same Inter stack | headings differentiate via weight 600 + tight tracking (−0.022em), **not** a second family |
|
||||||
|
| `--font-mono` | `"IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace` | code, tabular data |
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node create-crema-app/scripts/validate-theme.mjs lib-theme-graphite/theme.css
|
||||||
|
```
|
||||||
|
|
||||||
|
Passes the Crema v1 token contract (all Tier 1 tokens both modes, WCAG AA
|
||||||
|
contrast floors, no font `@import`, no root `font-size`).
|
||||||
426
theme.css
Normal file
426
theme.css
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
/*
|
||||||
|
* Graphite — achromatic precision.
|
||||||
|
*
|
||||||
|
* Dieter-Rams calm for operator/console/admin surfaces. The thesis is the
|
||||||
|
* ABSENCE of tint: every neutral in the theme is true achromatic (chroma 0),
|
||||||
|
* from the near-white grey field to the pure-white cards to the near-black
|
||||||
|
* ink. No blue-grey, no warm-grey, no atmosphere — just graphite on paper.
|
||||||
|
* ONE accent, an electric azure blue (hue ~258), is spent only where the eye
|
||||||
|
* must go: the primary action, links, selection, and the focus ring. Because
|
||||||
|
* every other pixel is colorless, that single blue points with unusual force.
|
||||||
|
*
|
||||||
|
* Dark mode is true dark GREY — not blue-black, not near-black drama. A
|
||||||
|
* charcoal field (L 0.19, chroma 0) with cards stepping visibly lighter
|
||||||
|
* (≥0.05 L) so altitude reads without shadows doing the work. The same azure
|
||||||
|
* carries the pointing.
|
||||||
|
*
|
||||||
|
* Type is Inter for both body and headings — headings differentiate through
|
||||||
|
* weight (600) and tight tracking, never through a second family. IBM Plex
|
||||||
|
* Mono for code. Dense/pro register: body 0.9375rem, labels 0.8125rem —
|
||||||
|
* built for tables, logs, and columns of numbers (tabular-nums is wired in).
|
||||||
|
* Radius is a compact 0.5rem; motion is product-fast (120/200/300ms) with
|
||||||
|
* springs aliased to standard eases — personality lives in speed, not bounce.
|
||||||
|
* Elevation is crisp and low; borders do most of the separating.
|
||||||
|
*
|
||||||
|
* Token contract matches lib-theme-caffe-florian/theme.css (Crema v1).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fonts: consuming apps load Inter + IBM Plex Mono at the top of their entry
|
||||||
|
* CSS (where @import statements are allowed), via the Google Fonts URL
|
||||||
|
* documented in this theme's README. Embedding the @import here — or
|
||||||
|
* smuggling the URL in a comment for a build script to lift — breaks
|
||||||
|
* PostCSS's ordering rules, so neither is done in this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--font-heading: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
||||||
|
|
||||||
|
/* Tailwind utility families. Distinct names so a consuming app's
|
||||||
|
* `@theme inline` can indirect to these (--font-heading/--font-sans collide
|
||||||
|
* with Tailwind's own font-* namespace). Mirror --font-heading/--font-sans. */
|
||||||
|
--heading-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--body-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
|
||||||
|
/* ── Colors ── true achromatic neutrals (chroma 0 everywhere), pure white
|
||||||
|
* cards, near-black ink, one electric azure accent. */
|
||||||
|
--background: oklch(0.97 0 0);
|
||||||
|
--foreground: oklch(0.2 0 0);
|
||||||
|
|
||||||
|
--card: oklch(1 0 0);
|
||||||
|
--card-foreground: oklch(0.2 0 0);
|
||||||
|
|
||||||
|
--popover: oklch(1 0 0);
|
||||||
|
--popover-foreground: oklch(0.2 0 0);
|
||||||
|
|
||||||
|
/* Primary — the sole color. Electric azure deep enough (L 0.5) to carry
|
||||||
|
* white labels well above the floor, saturated enough to read electric
|
||||||
|
* against a field that has no color at all. */
|
||||||
|
--primary: oklch(0.5 0.19 258);
|
||||||
|
--primary-foreground: oklch(1 0 0);
|
||||||
|
|
||||||
|
--secondary: oklch(0.94 0 0);
|
||||||
|
--secondary-foreground: oklch(0.27 0 0);
|
||||||
|
|
||||||
|
--muted: oklch(0.94 0 0);
|
||||||
|
--muted-foreground: oklch(0.45 0 0);
|
||||||
|
|
||||||
|
/* Accent — hover/selection tint: the faintest azure wash with deep azure
|
||||||
|
* ink. The only place besides primary where chroma is allowed to live. */
|
||||||
|
--accent: oklch(0.94 0.025 258);
|
||||||
|
--accent-foreground: oklch(0.4 0.16 258);
|
||||||
|
|
||||||
|
/* ── Status ── the only other hues in the theme, kept precise and
|
||||||
|
* unambiguous: red / green / amber / cyan-blue, each hue distinct from the
|
||||||
|
* azure primary. Destructive and info carry white on deep fills for alarm
|
||||||
|
* legibility; success and warning carry dark ink on brighter swatches. */
|
||||||
|
--destructive: oklch(0.55 0.21 27);
|
||||||
|
--destructive-foreground: oklch(0.98 0.01 27);
|
||||||
|
--success: oklch(0.6 0.14 150);
|
||||||
|
--success-foreground: oklch(0.17 0.03 150);
|
||||||
|
--warning: oklch(0.75 0.15 70);
|
||||||
|
--warning-foreground: oklch(0.22 0.05 70);
|
||||||
|
--info: oklch(0.5 0.12 230);
|
||||||
|
--info-foreground: oklch(0.98 0.005 230);
|
||||||
|
|
||||||
|
--border: oklch(0.2 0 0 / 0.12);
|
||||||
|
--input: oklch(0.2 0 0 / 0.14);
|
||||||
|
--ring: oklch(0.5 0.19 258 / 0.6);
|
||||||
|
|
||||||
|
/* Chart palette — azure lead, then a restrained cool spectrum (teal /
|
||||||
|
* violet / green / graphite slate). Hues spaced, adjacent-index lightness
|
||||||
|
* varied; the greys stay achromatic, the data gets the color. */
|
||||||
|
--chart-1: oklch(0.5 0.19 258);
|
||||||
|
--chart-2: oklch(0.68 0.11 195);
|
||||||
|
--chart-3: oklch(0.55 0.14 305);
|
||||||
|
--chart-4: oklch(0.64 0.12 150);
|
||||||
|
--chart-5: oklch(0.42 0 0);
|
||||||
|
|
||||||
|
--sidebar: oklch(0.955 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.2 0 0);
|
||||||
|
--sidebar-primary: oklch(0.5 0.19 258);
|
||||||
|
--sidebar-primary-foreground: oklch(1 0 0);
|
||||||
|
--sidebar-accent: oklch(0.94 0.025 258);
|
||||||
|
--sidebar-accent-foreground: oklch(0.4 0.16 258);
|
||||||
|
--sidebar-border: oklch(0.2 0 0 / 0.1);
|
||||||
|
--sidebar-ring: oklch(0.5 0.19 258 / 0.6);
|
||||||
|
|
||||||
|
/* Syntax — muted-precise. Low-chroma cool hues that read as annotation,
|
||||||
|
* not decoration; comments recede to achromatic grey. */
|
||||||
|
--syntax-keyword: oklch(0.45 0.13 290);
|
||||||
|
--syntax-string: oklch(0.47 0.1 150);
|
||||||
|
--syntax-number: oklch(0.5 0.11 70);
|
||||||
|
--syntax-comment: oklch(0.55 0 0);
|
||||||
|
--syntax-function: oklch(0.47 0.15 258);
|
||||||
|
--syntax-type: oklch(0.48 0.09 220);
|
||||||
|
|
||||||
|
/* ── Chat surfaces ──
|
||||||
|
* Contract for lib-chat-ui / lib-agent-ui / lib-agent-dock-ui. User bubble
|
||||||
|
* is a deep graphite panel with light text (neutral, so the azure stays
|
||||||
|
* scarce); assistant bubble is a clean white card with a hairline border. */
|
||||||
|
--chat-user-bg: oklch(0.25 0 0);
|
||||||
|
--chat-user-fg: oklch(0.97 0 0);
|
||||||
|
--chat-assistant-bg: oklch(1 0 0);
|
||||||
|
--chat-assistant-fg: oklch(0.2 0 0);
|
||||||
|
--chat-assistant-border: oklch(0.2 0 0 / 0.12);
|
||||||
|
|
||||||
|
/* Compact, precise radius. */
|
||||||
|
--radius: 0.5rem;
|
||||||
|
|
||||||
|
/* ── Motion ── product-fast. Springs alias to the standard eases so the
|
||||||
|
* theme stays calm — personality lives in the speed, never in bounce. */
|
||||||
|
--duration-fast: 120ms;
|
||||||
|
--duration-base: 200ms;
|
||||||
|
--duration-slow: 300ms;
|
||||||
|
--duration-slower: 420ms;
|
||||||
|
--duration-spring: 200ms;
|
||||||
|
|
||||||
|
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||||
|
--ease-decelerate: cubic-bezier(0, 0, 0, 1);
|
||||||
|
--ease-accelerate: cubic-bezier(0.3, 0, 1, 1);
|
||||||
|
--ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
|
||||||
|
|
||||||
|
--ease-spring-gentle: var(--ease-standard);
|
||||||
|
--ease-spring-snappy: var(--ease-decelerate);
|
||||||
|
--ease-spring-bouncy: var(--ease-standard);
|
||||||
|
|
||||||
|
/* ── Typography ── dense/pro register, fixed rem, full role ramp. */
|
||||||
|
--text-hero-size: 3rem;
|
||||||
|
--text-hero-lh: 1.05;
|
||||||
|
--text-display-size: 2.25rem;
|
||||||
|
--text-display-lh: 1.1;
|
||||||
|
--text-headline-size: 1.625rem;
|
||||||
|
--text-headline-lh: 1.25;
|
||||||
|
--text-title-size: 1.25rem;
|
||||||
|
--text-title-lh: 1.3;
|
||||||
|
--text-subtitle-size: 1.0625rem;
|
||||||
|
--text-subtitle-lh: 1.45;
|
||||||
|
--text-body-size: 0.9375rem;
|
||||||
|
--text-body-lh: 1.5;
|
||||||
|
--text-label-size: 0.8125rem;
|
||||||
|
--text-label-lh: 1.125rem;
|
||||||
|
--text-caption-size: 0.75rem;
|
||||||
|
--text-caption-lh: 1rem;
|
||||||
|
|
||||||
|
/* ── Elevation ── crisp, low, pure-neutral shadows. Borders separate;
|
||||||
|
* shadows only signal altitude. */
|
||||||
|
--elevation-0: none;
|
||||||
|
--elevation-1: 0 1px 2px oklch(0 0 0 / 0.05);
|
||||||
|
--elevation-2: 0 1px 3px oklch(0 0 0 / 0.07), 0 1px 2px oklch(0 0 0 / 0.04);
|
||||||
|
--elevation-3: 0 4px 8px -2px oklch(0 0 0 / 0.08), 0 2px 4px -2px oklch(0 0 0 / 0.04);
|
||||||
|
--elevation-4: 0 10px 20px -5px oklch(0 0 0 / 0.1), 0 4px 8px -4px oklch(0 0 0 / 0.05);
|
||||||
|
--elevation-5: 0 18px 36px -8px oklch(0 0 0 / 0.15);
|
||||||
|
|
||||||
|
--spacing: 0.25rem;
|
||||||
|
|
||||||
|
--border-width-thin: 0.5px;
|
||||||
|
--border-width-base: 1px;
|
||||||
|
--border-width-thick: 2px;
|
||||||
|
--border-width-heavy: 4px;
|
||||||
|
|
||||||
|
/* No glass — inert values so consuming apps referencing these get no-ops. */
|
||||||
|
--glass-blur: 0px;
|
||||||
|
--glass-saturate: 100%;
|
||||||
|
--glass-brightness: 1;
|
||||||
|
--glass-contrast: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dark mode — true dark grey, not blue-black. Charcoal field (chroma 0),
|
||||||
|
* cards stepping visibly lighter (bg 0.19 → card 0.24 → popover 0.26 →
|
||||||
|
* muted 0.28) so altitude reads as lightness. The azure brightens a step to
|
||||||
|
* keep its pop; everything else stays colorless.
|
||||||
|
*/
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.19 0 0);
|
||||||
|
--foreground: oklch(0.93 0 0);
|
||||||
|
|
||||||
|
--card: oklch(0.24 0 0);
|
||||||
|
--card-foreground: oklch(0.93 0 0);
|
||||||
|
|
||||||
|
--popover: oklch(0.26 0 0);
|
||||||
|
--popover-foreground: oklch(0.93 0 0);
|
||||||
|
|
||||||
|
--primary: oklch(0.55 0.19 258);
|
||||||
|
--primary-foreground: oklch(1 0 0);
|
||||||
|
|
||||||
|
--secondary: oklch(0.28 0 0);
|
||||||
|
--secondary-foreground: oklch(0.9 0 0);
|
||||||
|
|
||||||
|
--muted: oklch(0.28 0 0);
|
||||||
|
--muted-foreground: oklch(0.72 0 0);
|
||||||
|
|
||||||
|
--accent: oklch(0.3 0.05 258);
|
||||||
|
--accent-foreground: oklch(0.85 0.08 258);
|
||||||
|
|
||||||
|
--destructive: oklch(0.56 0.21 27);
|
||||||
|
--destructive-foreground: oklch(0.98 0.01 27);
|
||||||
|
--success: oklch(0.7 0.15 150);
|
||||||
|
--success-foreground: oklch(0.17 0.03 150);
|
||||||
|
--warning: oklch(0.78 0.14 70);
|
||||||
|
--warning-foreground: oklch(0.22 0.05 70);
|
||||||
|
--info: oklch(0.68 0.12 230);
|
||||||
|
--info-foreground: oklch(0.16 0.03 230);
|
||||||
|
|
||||||
|
--border: oklch(0.93 0 0 / 0.12);
|
||||||
|
--input: oklch(0.93 0 0 / 0.14);
|
||||||
|
--ring: oklch(0.62 0.17 258 / 0.6);
|
||||||
|
|
||||||
|
--chart-1: oklch(0.62 0.17 258);
|
||||||
|
--chart-2: oklch(0.74 0.11 195);
|
||||||
|
--chart-3: oklch(0.66 0.14 305);
|
||||||
|
--chart-4: oklch(0.72 0.13 150);
|
||||||
|
--chart-5: oklch(0.68 0 0);
|
||||||
|
|
||||||
|
--sidebar: oklch(0.21 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.93 0 0);
|
||||||
|
--sidebar-primary: oklch(0.55 0.19 258);
|
||||||
|
--sidebar-primary-foreground: oklch(1 0 0);
|
||||||
|
--sidebar-accent: oklch(0.3 0.05 258);
|
||||||
|
--sidebar-accent-foreground: oklch(0.85 0.08 258);
|
||||||
|
--sidebar-border: oklch(0.93 0 0 / 0.09);
|
||||||
|
--sidebar-ring: oklch(0.62 0.17 258 / 0.6);
|
||||||
|
|
||||||
|
--syntax-keyword: oklch(0.72 0.12 290);
|
||||||
|
--syntax-string: oklch(0.74 0.11 150);
|
||||||
|
--syntax-number: oklch(0.78 0.12 70);
|
||||||
|
--syntax-comment: oklch(0.6 0 0);
|
||||||
|
--syntax-function: oklch(0.72 0.13 258);
|
||||||
|
--syntax-type: oklch(0.74 0.09 220);
|
||||||
|
|
||||||
|
/* ── Chat surfaces (dark) ── */
|
||||||
|
--chat-user-bg: oklch(0.32 0 0);
|
||||||
|
--chat-user-fg: oklch(0.96 0 0);
|
||||||
|
--chat-assistant-bg: oklch(0.24 0 0);
|
||||||
|
--chat-assistant-fg: oklch(0.93 0 0);
|
||||||
|
--chat-assistant-border: oklch(0.93 0 0 / 0.12);
|
||||||
|
|
||||||
|
--elevation-1: 0 1px 2px oklch(0 0 0 / 0.3);
|
||||||
|
--elevation-2: 0 1px 3px oklch(0 0 0 / 0.4), 0 1px 2px oklch(0 0 0 / 0.25);
|
||||||
|
--elevation-3: 0 4px 8px -2px oklch(0 0 0 / 0.4), 0 2px 4px -2px oklch(0 0 0 / 0.25);
|
||||||
|
--elevation-4: 0 10px 20px -5px oklch(0 0 0 / 0.45), 0 4px 8px -4px oklch(0 0 0 / 0.25);
|
||||||
|
--elevation-5: 0 18px 36px -8px oklch(0 0 0 / 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @layer base — Graphite's achromatic-precision philosophy.
|
||||||
|
*
|
||||||
|
* No glass, no gradients, no tint. One clean grotesque at two weights,
|
||||||
|
* colorless surfaces hairline-separated, and the azure reserved for focus,
|
||||||
|
* selection, links, and the primary action. Numbers align in columns because
|
||||||
|
* operators read columns.
|
||||||
|
*/
|
||||||
|
@layer base {
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
letter-spacing: -0.006em;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: var(--text-body-size);
|
||||||
|
line-height: var(--text-body-lh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headings — same family as body; hierarchy is weight + tracking. */
|
||||||
|
h1, h2, h3, h4,
|
||||||
|
[data-slot="card-title"],
|
||||||
|
[data-slot="dialog-title"],
|
||||||
|
[data-slot="sheet-title"],
|
||||||
|
[data-slot="alert-dialog-title"] {
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: -0.022em;
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
code, kbd, samp, pre,
|
||||||
|
[data-slot="code"],
|
||||||
|
[data-slot="code-block"] {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code — achromatic chip, never accent-colored. */
|
||||||
|
:not(pre) > code {
|
||||||
|
padding: 0.1em 0.35em;
|
||||||
|
border-radius: calc(var(--radius) * 0.5);
|
||||||
|
background: color-mix(in oklch, var(--foreground) 6%, transparent);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection — the azure moment. */
|
||||||
|
::selection {
|
||||||
|
background: color-mix(in oklch, var(--primary) 30%, transparent);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links — azure, underline-on-hover. */
|
||||||
|
a {
|
||||||
|
color: color-mix(in oklch, var(--primary) 70%, var(--foreground));
|
||||||
|
text-decoration-color: color-mix(in oklch, var(--primary) 40%, transparent);
|
||||||
|
text-underline-offset: 0.2em;
|
||||||
|
transition: text-decoration-color var(--duration-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="sidebar"] {
|
||||||
|
background: var(--sidebar);
|
||||||
|
border-right: var(--border-width-base) solid var(--sidebar-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dense utility — operators read columns of numbers. Tables and anything
|
||||||
|
* marked .num get lining, equal-width figures so columns align. */
|
||||||
|
[data-slot="table"] {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
.num {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Focus-visible — keyboard-first is a Graphite signature. Buttons get a
|
||||||
|
* crisp 2px azure outline with 2px offset; inputs get a flush 1px ring plus
|
||||||
|
* a border-color change (handled in the input block below). Placed outside
|
||||||
|
* @layer base so it beats Tailwind's default ring utilities.
|
||||||
|
*/
|
||||||
|
[data-slot="button"]:focus-visible {
|
||||||
|
outline: 2px solid var(--primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
[data-slot="input"]:focus-visible,
|
||||||
|
[data-slot="textarea"]:focus-visible,
|
||||||
|
[data-slot="select-trigger"]:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 1px var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons — quiet weight; opacity absorb on press, product-fast. */
|
||||||
|
[data-slot="button"] {
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: -0.006em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card titles — pinned to the subtitle role so the scale is the theme's
|
||||||
|
* decision, not the card primitive's default. */
|
||||||
|
[data-slot="card-title"] {
|
||||||
|
font-size: var(--text-subtitle-size);
|
||||||
|
line-height: var(--text-subtitle-lh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Touch targets — desktop keeps the dense ladder; coarse pointers get the
|
||||||
|
* 44px floor on every control. */
|
||||||
|
@media (pointer: coarse) {
|
||||||
|
[data-slot="button"] {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
min-width: 2.75rem;
|
||||||
|
}
|
||||||
|
[data-slot="input"],
|
||||||
|
[data-slot="select-trigger"] {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-slot="button"]:hover:not(:disabled) {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
[data-slot="button"]:active:not(:disabled) {
|
||||||
|
opacity: 0.82;
|
||||||
|
transform: scale(0.99);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inputs — white card surface, hairline border, azure focus as a flush ring. */
|
||||||
|
[data-slot="input"],
|
||||||
|
[data-slot="textarea"],
|
||||||
|
[data-slot="select-trigger"] {
|
||||||
|
background: var(--card);
|
||||||
|
border: var(--border-width-base) solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
color: var(--foreground);
|
||||||
|
transition:
|
||||||
|
border-color var(--duration-fast) var(--ease-standard),
|
||||||
|
box-shadow var(--duration-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
[data-slot="input"]::placeholder,
|
||||||
|
[data-slot="textarea"]::placeholder {
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
[data-slot="input"]:hover:not(:disabled),
|
||||||
|
[data-slot="textarea"]:hover:not(:disabled),
|
||||||
|
[data-slot="select-trigger"]:hover:not(:disabled) {
|
||||||
|
border-color: color-mix(in oklch, var(--foreground) 24%, transparent);
|
||||||
|
}
|
||||||
|
[data-slot="input"]:focus,
|
||||||
|
[data-slot="textarea"]:focus,
|
||||||
|
[data-slot="select-trigger"]:focus {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 1px var(--primary);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user