lib-theme-porcelain: initial import (warm-consumer set, 2026-07)
Includes the 2026-07-07 UX sweep: neutral inline-code chips (accent reserved for actions/links/focus), dark-mode card/popover lightness lift for visible surface separation, 44px touch-target floor on coarse pointers, card-title pinned to a named ramp role. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
39
README.md
Normal file
39
README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# lib-theme-porcelain
|
||||||
|
|
||||||
|
A calm, neutral **bone-china** theme warmed by a single restrained clay accent — the "iCloud / Apple-Settings calm, but warm" answer for a back-of-house personal-cloud utility (me.sky-ai.com). Cool porcelain off-white field (never a cream wash), warm charcoal ink, one muted terracotta accent used on ≤10% of the surface (primary action, selection, focus, links). Albert Sans for body/UI, Newsreader as a restrained serif for headings, JetBrains Mono for code. Whisper-soft neutral elevation, calm-modern 0.875rem radius.
|
||||||
|
|
||||||
|
## Design philosophy
|
||||||
|
|
||||||
|
- **Surface treatment:** Porcelain. A cool off-white field with pure-white cards, hairline borders and whisper-soft neutral shadows — calm resting tiles, not floating chrome. No glass, no offsets.
|
||||||
|
- **Color:** Neutral-dominant. The background is held OUT of the AI cream/sand band by keeping chroma to a whisper (≤0.004) and the hue faintly cool (~265, bone china) at very high L (0.985) — clean-neutral, not warm parchment. Ink is a warm-leaning near-black charcoal (hue ~55). The one warm note is a muted clay primary (L≈0.54, C≈0.11, H≈47), deepened so a white label clears 4.5:1. Status quartet is spaced clearly in hue (clay 47 / red 27 / green 150 / amber 78 / blue 245) so nothing muddies against the accent.
|
||||||
|
- **Typography:** Newsreader for headings (restrained modern serif, kept calm at weight 560 — not editorial-loud). Albert Sans for body and UI. JetBrains Mono for code. Product register: fixed rem sizes, full `--text-*` role ramp.
|
||||||
|
- **Motion:** Calm and product-fast — 150 / 300 / 420ms. Springs alias to standard easings (no overshoot); personality lives in the values, not in bounce.
|
||||||
|
- **Elevation:** Whisper-soft neutral shadows (near-black at very low alpha), low-key. Same altitude, same shadow.
|
||||||
|
- **Radius:** 0.875rem — calm-modern.
|
||||||
|
- **Borders:** Hairline (1px, alpha 0.10 of the ink) so separation stays subtle.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
In a consuming app's `app.css`:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import "../../lib-theme-porcelain/theme.css";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fonts
|
||||||
|
|
||||||
|
The theme does not load fonts (contract ban — fonts load from the consuming app's entry CSS). Add this Google Fonts `@import` at the very top of the app's entry CSS:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Albert+Sans:wght@400;500;600;700&family=Newsreader:opsz,wght@6..72,400;6..72,500;6..72,560;6..72,600&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||||
|
```
|
||||||
|
|
||||||
|
Or install locally via Fontsource:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @fontsource-variable/albert-sans @fontsource-variable/newsreader @fontsource-variable/jetbrains-mono
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dark mode
|
||||||
|
|
||||||
|
Supported via class-based `.dark`. Warm graphite (`oklch(0.20 0.006 60)`) — very low chroma at a warm hue so it reads as a neutral warm charcoal, not blue-black and not umber-brown. The clay accent brightens to L≈0.66 and carries dark ink (dark-ink-on-bright, per the contract); shadows deepen.
|
||||||
457
theme.css
Normal file
457
theme.css
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
/*
|
||||||
|
* Porcelain — calm, neutral bone-china surfaces warmed by a single restrained
|
||||||
|
* clay accent. Built as the "iCloud / Apple-Settings calm, but warm" answer for
|
||||||
|
* a back-of-house personal-cloud utility: quiet chrome, confident whitespace,
|
||||||
|
* hairline separation, one warm note that points at the next action.
|
||||||
|
*
|
||||||
|
* Palette rationale. The field is a true off-white porcelain, not a cream band:
|
||||||
|
* chroma is held to a whisper (≤0.004) and the hue leans faintly COOL (~265,
|
||||||
|
* bone china) so the background reads clean-neutral at very high L (0.985) —
|
||||||
|
* deliberately above and to the cool side of the banned AI cream/sand region
|
||||||
|
* (L 0.84–0.97, warm hue 40–100). Cards step up to pure white; separation is a
|
||||||
|
* hairline border plus the faintest neutral elevation. Ink is a warm-leaning
|
||||||
|
* near-black charcoal (hue ~55) — the one place quiet warmth lives in the text.
|
||||||
|
* The lone accent is a muted clay / soft terracotta (hue ~47), a restrained
|
||||||
|
* echo of siena used on ≤10% of the surface: primary action, selection, focus,
|
||||||
|
* links — never as a wash. Status quartet is held clearly apart in hue (clay 47
|
||||||
|
* / red 27 / green 150 / amber 78 / blue 245) so meaning never muddies against
|
||||||
|
* the warm accent.
|
||||||
|
*
|
||||||
|
* Token contract matches lib-theme-caffe-florian/theme.css (Crema v1).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fonts: consuming apps should load Albert Sans + Newsreader + JetBrains 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
|
||||||
|
* inside this file — or smuggling the URL in a comment for a build script to
|
||||||
|
* lift — breaks PostCSS's ordering rules and is banned by the contract, so
|
||||||
|
* neither is done here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--font-sans: "Albert Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
--font-heading: "Newsreader", Georgia, "Times New Roman", serif;
|
||||||
|
--font-mono: "JetBrains Mono", ui-monospace, 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: "Newsreader", Georgia, "Times New Roman", serif;
|
||||||
|
--body-family: "Albert Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
|
||||||
|
/* ── Colors ── cool bone-china porcelain field, warm charcoal ink, one
|
||||||
|
* muted clay accent. Kept out of the cream band by very low chroma and a
|
||||||
|
* faint cool (bone) hue at high L. */
|
||||||
|
--background: oklch(0.985 0.004 265);
|
||||||
|
--foreground: oklch(0.21 0.008 55);
|
||||||
|
|
||||||
|
--card: oklch(1 0 0);
|
||||||
|
--card-foreground: oklch(0.21 0.008 55);
|
||||||
|
|
||||||
|
--popover: oklch(0.995 0.003 265);
|
||||||
|
--popover-foreground: oklch(0.21 0.008 55);
|
||||||
|
|
||||||
|
/* Primary — muted clay. Deepened so white label clears the 4.5:1 floor
|
||||||
|
* (5.13:1) while still reading calm and premium, not loud. */
|
||||||
|
--primary: oklch(0.54 0.11 47);
|
||||||
|
--primary-foreground: oklch(0.99 0.004 60);
|
||||||
|
|
||||||
|
--secondary: oklch(0.955 0.004 265);
|
||||||
|
--secondary-foreground: oklch(0.30 0.008 55);
|
||||||
|
|
||||||
|
--muted: oklch(0.955 0.004 265);
|
||||||
|
--muted-foreground: oklch(0.50 0.008 55);
|
||||||
|
|
||||||
|
/* Accent — faint clay tint surface with deep clay ink. The single warm
|
||||||
|
* note; used for hovers/selection, never a body wash. */
|
||||||
|
--accent: oklch(0.94 0.02 47);
|
||||||
|
--accent-foreground: oklch(0.42 0.10 47);
|
||||||
|
|
||||||
|
--destructive: oklch(0.55 0.20 27);
|
||||||
|
--destructive-foreground: oklch(0.99 0.004 60);
|
||||||
|
/* Success — held at hue 150 (well clear of clay 47), dark ink on top. */
|
||||||
|
--success: oklch(0.57 0.13 150);
|
||||||
|
--success-foreground: oklch(0.16 0.03 150);
|
||||||
|
/* Warning — amber ochre, dark ink on top. */
|
||||||
|
--warning: oklch(0.75 0.14 78);
|
||||||
|
--warning-foreground: oklch(0.22 0.04 78);
|
||||||
|
/* Info — calm blue, light label on top. */
|
||||||
|
--info: oklch(0.53 0.145 245);
|
||||||
|
--info-foreground: oklch(0.99 0.004 60);
|
||||||
|
|
||||||
|
--border: oklch(0.21 0.008 55 / 0.10);
|
||||||
|
--input: oklch(0.21 0.008 55 / 0.10);
|
||||||
|
--ring: oklch(0.54 0.11 47 / 0.5);
|
||||||
|
|
||||||
|
/* Chart palette — clay lead, then hues spaced ≥60° apart (clay / teal /
|
||||||
|
* blue / violet / green), adjacent lightness varied. Calm, low-shout. */
|
||||||
|
--chart-1: oklch(0.62 0.11 47);
|
||||||
|
--chart-2: oklch(0.60 0.08 195);
|
||||||
|
--chart-3: oklch(0.55 0.10 255);
|
||||||
|
--chart-4: oklch(0.66 0.09 315);
|
||||||
|
--chart-5: oklch(0.64 0.10 150);
|
||||||
|
|
||||||
|
--sidebar: oklch(0.97 0.004 265);
|
||||||
|
--sidebar-foreground: oklch(0.21 0.008 55);
|
||||||
|
--sidebar-primary: oklch(0.54 0.11 47);
|
||||||
|
--sidebar-primary-foreground: oklch(0.99 0.004 60);
|
||||||
|
--sidebar-accent: oklch(0.94 0.02 47);
|
||||||
|
--sidebar-accent-foreground: oklch(0.42 0.10 47);
|
||||||
|
--sidebar-border: oklch(0.21 0.008 55 / 0.08);
|
||||||
|
--sidebar-ring: oklch(0.54 0.11 47 / 0.5);
|
||||||
|
|
||||||
|
--syntax-keyword: oklch(0.50 0.11 47);
|
||||||
|
--syntax-string: oklch(0.50 0.11 150);
|
||||||
|
--syntax-number: oklch(0.54 0.11 78);
|
||||||
|
--syntax-comment: oklch(0.58 0.008 60);
|
||||||
|
--syntax-function: oklch(0.50 0.13 255);
|
||||||
|
--syntax-type: oklch(0.52 0.08 210);
|
||||||
|
|
||||||
|
/* ── Chat surfaces ──
|
||||||
|
* Contract for lib-agent-chat-ui / lib-agent-dock-ui. User bubble is a
|
||||||
|
* filled deep clay with a light label; assistant bubble is a clean white
|
||||||
|
* card with a hairline border. */
|
||||||
|
--chat-user-bg: oklch(0.50 0.11 47);
|
||||||
|
--chat-user-fg: oklch(0.99 0.004 60);
|
||||||
|
--chat-assistant-bg: oklch(1 0 0);
|
||||||
|
--chat-assistant-fg: oklch(0.21 0.008 55);
|
||||||
|
--chat-assistant-border: oklch(0.21 0.008 55 / 0.10);
|
||||||
|
|
||||||
|
/* Calm-modern radius. */
|
||||||
|
--radius: 0.875rem;
|
||||||
|
|
||||||
|
/* ── Motion ── calm, product-fast. */
|
||||||
|
--duration-fast: 150ms;
|
||||||
|
--duration-base: 300ms;
|
||||||
|
--duration-slow: 420ms;
|
||||||
|
--duration-slower: 560ms;
|
||||||
|
--duration-spring: 300ms;
|
||||||
|
|
||||||
|
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
--ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
|
||||||
|
--ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
|
||||||
|
--ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
|
||||||
|
|
||||||
|
/* Calm theme — springs alias to standard easings (no overshoot). Personality
|
||||||
|
* lives in the values, never in absence (the avalon rule). */
|
||||||
|
--ease-spring-gentle: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
--ease-spring-snappy: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
--ease-spring-bouncy: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
|
/* ── Typography ── product register, fixed rem sizes, full role ramp.
|
||||||
|
* Newsreader supplies a restrained modern-serif heading; Albert Sans is the
|
||||||
|
* humanist body/UI sans. */
|
||||||
|
--text-hero-size: 3.5rem;
|
||||||
|
--text-hero-lh: 3.75rem;
|
||||||
|
--text-display-size: 3rem;
|
||||||
|
--text-display-lh: 3.25rem;
|
||||||
|
--text-headline-size: 2rem;
|
||||||
|
--text-headline-lh: 2.375rem;
|
||||||
|
--text-title-size: 1.5rem;
|
||||||
|
--text-title-lh: 1.875rem;
|
||||||
|
--text-subtitle-size: 1.1875rem;
|
||||||
|
--text-subtitle-lh: 1.625rem;
|
||||||
|
--text-body-size: 1.0625rem;
|
||||||
|
--text-body-lh: 1.625rem;
|
||||||
|
--text-label-size: 0.9375rem;
|
||||||
|
--text-label-lh: 1.25rem;
|
||||||
|
--text-caption-size: 0.8125rem;
|
||||||
|
--text-caption-lh: 1.125rem;
|
||||||
|
|
||||||
|
/* ── Elevation ── whisper-soft neutral shadows, near-black at very low
|
||||||
|
* alpha. Calm resting cards, low-key lift. */
|
||||||
|
--elevation-0: none;
|
||||||
|
--elevation-1: 0 1px 2px oklch(0.21 0.008 55 / 0.04);
|
||||||
|
--elevation-2: 0 1px 3px oklch(0.21 0.008 55 / 0.06), 0 1px 2px oklch(0.21 0.008 55 / 0.03);
|
||||||
|
--elevation-3: 0 4px 10px -3px oklch(0.21 0.008 55 / 0.07), 0 2px 4px -2px oklch(0.21 0.008 55 / 0.04);
|
||||||
|
--elevation-4: 0 12px 28px -6px oklch(0.21 0.008 55 / 0.09), 0 4px 8px -4px oklch(0.21 0.008 55 / 0.04);
|
||||||
|
--elevation-5: 0 24px 48px -12px oklch(0.21 0.008 55 / 0.14);
|
||||||
|
|
||||||
|
--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 — warm graphite (#232120-ish), not blue-black and not umber-brown.
|
||||||
|
* Very low chroma at a warm hue (~60) keeps it a neutral warm charcoal; the clay
|
||||||
|
* accent brightens just enough and carries dark ink per the contract.
|
||||||
|
*/
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.20 0.006 60);
|
||||||
|
--foreground: oklch(0.94 0.006 70);
|
||||||
|
|
||||||
|
--card: oklch(0.26 0.007 60);
|
||||||
|
--card-foreground: oklch(0.94 0.006 70);
|
||||||
|
|
||||||
|
--popover: oklch(0.275 0.007 60);
|
||||||
|
--popover-foreground: oklch(0.94 0.006 70);
|
||||||
|
|
||||||
|
--primary: oklch(0.66 0.12 50);
|
||||||
|
--primary-foreground: oklch(0.18 0.02 50);
|
||||||
|
|
||||||
|
--secondary: oklch(0.28 0.007 60);
|
||||||
|
--secondary-foreground: oklch(0.90 0.006 70);
|
||||||
|
|
||||||
|
--muted: oklch(0.28 0.007 60);
|
||||||
|
--muted-foreground: oklch(0.70 0.008 65);
|
||||||
|
|
||||||
|
--accent: oklch(0.32 0.04 50);
|
||||||
|
--accent-foreground: oklch(0.85 0.08 50);
|
||||||
|
|
||||||
|
--destructive: oklch(0.62 0.19 27);
|
||||||
|
--destructive-foreground: oklch(0.16 0.02 27);
|
||||||
|
--success: oklch(0.70 0.15 150);
|
||||||
|
--success-foreground: oklch(0.16 0.03 150);
|
||||||
|
--warning: oklch(0.80 0.14 78);
|
||||||
|
--warning-foreground: oklch(0.20 0.04 78);
|
||||||
|
--info: oklch(0.72 0.13 245);
|
||||||
|
--info-foreground: oklch(0.18 0.03 245);
|
||||||
|
|
||||||
|
--border: oklch(0.95 0.006 70 / 0.10);
|
||||||
|
--input: oklch(0.95 0.006 70 / 0.10);
|
||||||
|
--ring: oklch(0.66 0.12 50 / 0.5);
|
||||||
|
|
||||||
|
--chart-1: oklch(0.70 0.12 50);
|
||||||
|
--chart-2: oklch(0.70 0.08 195);
|
||||||
|
--chart-3: oklch(0.68 0.10 255);
|
||||||
|
--chart-4: oklch(0.74 0.09 315);
|
||||||
|
--chart-5: oklch(0.72 0.11 150);
|
||||||
|
|
||||||
|
--sidebar: oklch(0.22 0.006 60);
|
||||||
|
--sidebar-foreground: oklch(0.94 0.006 70);
|
||||||
|
--sidebar-primary: oklch(0.66 0.12 50);
|
||||||
|
--sidebar-primary-foreground: oklch(0.18 0.02 50);
|
||||||
|
--sidebar-accent: oklch(0.32 0.04 50);
|
||||||
|
--sidebar-accent-foreground: oklch(0.85 0.08 50);
|
||||||
|
--sidebar-border: oklch(0.95 0.006 70 / 0.08);
|
||||||
|
--sidebar-ring: oklch(0.66 0.12 50 / 0.5);
|
||||||
|
|
||||||
|
--syntax-keyword: oklch(0.74 0.12 50);
|
||||||
|
--syntax-string: oklch(0.74 0.13 150);
|
||||||
|
--syntax-number: oklch(0.78 0.13 78);
|
||||||
|
--syntax-comment: oklch(0.58 0.008 60);
|
||||||
|
--syntax-function: oklch(0.72 0.13 255);
|
||||||
|
--syntax-type: oklch(0.72 0.08 210);
|
||||||
|
|
||||||
|
/* ── Chat surfaces (dark) ── deep clay bubble, dark ink label
|
||||||
|
* (dark-ink-on-bright, 5.56:1). */
|
||||||
|
--chat-user-bg: oklch(0.64 0.115 50);
|
||||||
|
--chat-user-fg: oklch(0.16 0.02 50);
|
||||||
|
--chat-assistant-bg: oklch(0.26 0.007 60);
|
||||||
|
--chat-assistant-fg: oklch(0.94 0.006 70);
|
||||||
|
--chat-assistant-border: oklch(0.95 0.006 70 / 0.10);
|
||||||
|
|
||||||
|
--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 10px oklch(0 0 0 / 0.35), 0 2px 4px oklch(0 0 0 / 0.25);
|
||||||
|
--elevation-4: 0 12px 28px oklch(0 0 0 / 0.4), 0 4px 8px oklch(0 0 0 / 0.25);
|
||||||
|
--elevation-5: 0 24px 48px oklch(0 0 0 / 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @layer base — Porcelain's calm-utility philosophy.
|
||||||
|
*
|
||||||
|
* No glass, no offsets. Humanist sans body; restrained modern-serif headings
|
||||||
|
* used sparingly. Cards sit on the porcelain field with a hairline border and a
|
||||||
|
* whisper-soft neutral shadow — calm resting surfaces, not floating chrome. The
|
||||||
|
* clay accent points; everything else is neutral.
|
||||||
|
*/
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Serif headings — Newsreader, kept calm (not editorial-loud): moderate
|
||||||
|
* weight, tight-but-gentle 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: 560;
|
||||||
|
letter-spacing: -0.012em;
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
code, kbd, samp, pre,
|
||||||
|
[data-slot="code"],
|
||||||
|
[data-slot="code-block"] {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code — neutral ink on a faint chip; the clay accent stays
|
||||||
|
* reserved for actions, links and focus. */
|
||||||
|
:not(pre) > code {
|
||||||
|
padding: 0.1em 0.35em;
|
||||||
|
border-radius: calc(var(--radius) * 0.4);
|
||||||
|
background: color-mix(in oklch, var(--foreground) 7%, transparent);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection — the one warm note, faint clay wash. */
|
||||||
|
::selection {
|
||||||
|
background: color-mix(in oklch, var(--primary) 18%, transparent);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links — clay, quietly underlined on hover. */
|
||||||
|
a {
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration-color: color-mix(in oklch, var(--primary) 35%, transparent);
|
||||||
|
text-underline-offset: 0.2em;
|
||||||
|
transition: text-decoration-color var(--duration-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card & overlay surfaces — hairline border + whisper elevation so cards
|
||||||
|
* read as calm resting tiles on porcelain. */
|
||||||
|
[data-slot="card"],
|
||||||
|
[data-slot="popover-content"],
|
||||||
|
[data-slot="dropdown-menu-content"],
|
||||||
|
[data-slot="context-menu-content"],
|
||||||
|
[data-slot="menubar-content"],
|
||||||
|
[data-slot="hover-card-content"],
|
||||||
|
[data-slot="select-content"],
|
||||||
|
[data-slot="combobox-content"],
|
||||||
|
[data-slot="command"],
|
||||||
|
[data-slot="tooltip-content"],
|
||||||
|
[data-slot="sheet-content"],
|
||||||
|
[data-slot="dialog-content"],
|
||||||
|
[data-slot="alert-dialog-content"],
|
||||||
|
[data-slot="drawer-content"] {
|
||||||
|
border: var(--border-width-base) solid var(--border);
|
||||||
|
box-shadow: var(--elevation-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark [data-slot="card"],
|
||||||
|
.dark [data-slot="popover-content"],
|
||||||
|
.dark [data-slot="dropdown-menu-content"],
|
||||||
|
.dark [data-slot="context-menu-content"],
|
||||||
|
.dark [data-slot="menubar-content"],
|
||||||
|
.dark [data-slot="hover-card-content"],
|
||||||
|
.dark [data-slot="select-content"],
|
||||||
|
.dark [data-slot="combobox-content"],
|
||||||
|
.dark [data-slot="command"],
|
||||||
|
.dark [data-slot="tooltip-content"],
|
||||||
|
.dark [data-slot="sheet-content"],
|
||||||
|
.dark [data-slot="dialog-content"],
|
||||||
|
.dark [data-slot="alert-dialog-content"],
|
||||||
|
.dark [data-slot="drawer-content"] {
|
||||||
|
box-shadow: var(--elevation-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="button"] {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="sidebar"] {
|
||||||
|
background: var(--sidebar);
|
||||||
|
border-right: var(--border-width-base) solid var(--sidebar-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlayered component polish — placed outside @layer base so it beats Tailwind's
|
||||||
|
* default utility classes shipped with shadcn. Porcelain keeps controls calm and
|
||||||
|
* comfortably sized with clay focus: inputs are ≥44px everywhere; buttons keep
|
||||||
|
* their density under a mouse and clear the 44px floor on coarse pointers.
|
||||||
|
*/
|
||||||
|
[data-slot="button"]:hover:not(:disabled) {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
[data-slot="button"]:active:not(:disabled) {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(0.99);
|
||||||
|
}
|
||||||
|
[data-slot="button"]:focus-visible {
|
||||||
|
outline: 2px solid var(--primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="input"],
|
||||||
|
[data-slot="textarea"],
|
||||||
|
[data-slot="select-trigger"] {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
padding-inline: 0.875rem;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
background: var(--card);
|
||||||
|
border: 1px 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="textarea"] {
|
||||||
|
min-height: 6.25rem;
|
||||||
|
padding-block: 0.75rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
[data-slot="input"]::placeholder,
|
||||||
|
[data-slot="textarea"]::placeholder {
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
[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) 22%, transparent);
|
||||||
|
}
|
||||||
|
[data-slot="input"]:focus,
|
||||||
|
[data-slot="input"]:focus-visible,
|
||||||
|
[data-slot="textarea"]:focus,
|
||||||
|
[data-slot="textarea"]:focus-visible,
|
||||||
|
[data-slot="select-trigger"]:focus,
|
||||||
|
[data-slot="select-trigger"]:focus-visible {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 1px var(--primary);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-slot="card"] {
|
||||||
|
background: var(--card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
transition:
|
||||||
|
border-color var(--duration-base) var(--ease-standard),
|
||||||
|
box-shadow var(--duration-base) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 — buttons clear the 44px floor on coarse pointers
|
||||||
|
* (inputs are already ≥44px everywhere above). */
|
||||||
|
@media (pointer: coarse) {
|
||||||
|
[data-slot="button"] {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
min-width: 2.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user