15 lines
19 KiB
JSON
15 lines
19 KiB
JSON
{
|
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
"name": "foundation",
|
|
"type": "registry:style",
|
|
"title": "Beautiful UI foundation",
|
|
"description": "Design tokens (:root/.dark), the @theme mapping, base rules, the primitive-* spacing utilities, and shared keyframes every component needs. Then add `@import \"./beautifui/foundation.css\";` to your app's globals.css.",
|
|
"files": [
|
|
{
|
|
"path": "app/beautifui/foundation.css",
|
|
"content": "@import \"tailwindcss\";\n/* shadow-plugin (flornkm) — smooth, layered shadow stacks. The unprefixed\n * entry maps them onto Tailwind's --shadow-xs…2xl scale; our semantic tokens\n * below build on those values so the whole app picks up the smooth shadows. */\n@import \"shadow-plugin/unprefixed\";\n\n@custom-variant dark (&:where(.dark, .dark *));\n\n/* GENERIC FOUNDATION — tokens, @theme, base rules, the primitive-*\n * spacing utilities, and shared keyframes below are needed by EVERY\n * component. Blocks tagged COMPONENT-SPECIFIC further down belong to\n * one primitive each. */\n/* ──────────────────────────────────────────────────────────\n * Beautiful UI — design tokens\n * Cool near-white canvas, white cards, hairline borders, layered\n * single-digit-opacity shadows, a neutral ink ramp, and semantic\n * color used sparingly as a condiment.\n * ────────────────────────────────────────────────────────── */\n\n:root {\n /* surfaces — cool, blue-tinted neutrals */\n --page: oklch(0.985 0.001 286.376);\n --canvas: oklch(0.961 0.002 247.84);\n --surface: oklch(1 0 0);\n --inset: oklch(0.979 0.002 247.839);\n --hover: oklch(0.97 0.002 247.839);\n --hover-2: oklch(0.933 0.003 247.86);\n\n /* ink ramp */\n --ink: oklch(0.247 0.006 258.361);\n --ink-2: oklch(0.506 0.01 264.477);\n --ink-3: oklch(0.695 0.009 264.505);\n\n /* borders — solid and crisp, not alpha */\n --line: oklch(0.946 0.003 264.542);\n --line-strong: oklch(0.912 0.005 258.326);\n --line-soft: oklch(0.966 0.002 264.542);\n --grid-line: color-mix(in srgb, var(--line) 78%, transparent);\n --field: oklch(0.961 0.001 286.375);\n --stripe: oklch(0.405 0 0 / 0.075);\n --stripe-bg: oklch(0.97 0 0);\n\n /* accent */\n --accent: oklch(0.626 0.205 254.947);\n --accent-ink: oklch(0.556 0.187 255.617);\n --accent-tint: oklch(0.96 0.019 252.878);\n\n /* semantic */\n --green: oklch(0.603 0.155 150.883);\n --green-tint: oklch(0.958 0.017 159.118);\n --orange: oklch(0.689 0.179 49.902);\n --orange-tint: oklch(0.964 0.021 67.581);\n --red: oklch(0.621 0.192 23.042);\n --red-tint: oklch(0.956 0.017 17.462);\n\n /* chart overlays — neutral system colors with enough contrast on either theme */\n --tooltip-bg: oklch(0.272 0.008 264.435);\n --tooltip-fg: oklch(0.976 0.002 247.839);\n --tooltip-muted: oklch(0.731 0.008 260.731);\n --tooltip-border: oklch(0.356 0.007 264.474);\n\n /* shadows — our solid hairline ring + shadow-plugin's smooth layered stacks\n * (--shadow-xs…lg come from shadow-plugin/unprefixed) so every surface reads\n * consistently across the app. */\n --shadow-hairline: 0 0 0 1px var(--line);\n --shadow-btn: 0 0 0 1px var(--line-strong), var(--shadow-xs);\n --shadow-card: 0 0 0 1px var(--line), var(--shadow-sm);\n --shadow-raised: 0 0 0 1px var(--line), var(--shadow-md);\n --shadow-overlay: 0 0 0 1px var(--line), var(--shadow-lg);\n --shadow-inset-field: inset 0 1px 2px oklch(0 0 0 / 0.12);\n}\n\n.dark {\n --page: oklch(0.209 0.004 264.477);\n --canvas: oklch(0.231 0.004 264.487);\n --surface: oklch(0.26 0.006 271.191);\n --inset: oklch(0.243 0.004 264.492);\n --hover: oklch(0.289 0.006 271.22);\n --hover-2: oklch(0.318 0.007 274.747);\n\n --ink: oklch(0.964 0.002 247.839);\n --ink-2: oklch(0.731 0.008 260.731);\n --ink-3: oklch(0.541 0.01 264.484);\n\n --line: oklch(0.308 0.006 258.354);\n --line-strong: oklch(0.356 0.007 264.474);\n --line-soft: oklch(0.278 0.006 258.354);\n --field: oklch(0.293 0.006 271.223);\n --stripe: oklch(1 0 0 / 0.055);\n --stripe-bg: oklch(0.226 0.004 264.485);\n\n --accent: oklch(0.68 0.173 253.301);\n --accent-ink: oklch(0.788 0.113 248.33);\n --accent-tint: oklch(0.68 0.173 253.301 / 0.16);\n\n --green: oklch(0.705 0.154 153.814);\n --green-tint: oklch(0.705 0.154 153.814 / 0.14);\n --orange: oklch(0.746 0.156 55.642);\n --orange-tint: oklch(0.746 0.156 55.642 / 0.14);\n --red: oklch(0.666 0.18 21.433);\n --red-tint: oklch(0.666 0.18 21.433 / 0.14);\n\n --tooltip-bg: oklch(0.182 0.004 264.459);\n --tooltip-fg: oklch(0.964 0.002 247.839);\n --tooltip-muted: oklch(0.731 0.008 260.731);\n --tooltip-border: oklch(0.308 0.006 258.354);\n\n --shadow-hairline: 0 0 0 1px var(--line);\n /* Elevation rings go to a subtle *light* hairline in dark mode: the ring\n * paints over the surface behind the element, so a low-alpha white edge\n * dissolves into the shadow as one continuous stroke (per smooth-shadow-ring),\n * graduated by how far the surface floats. */\n --shadow-btn: 0 0 0 1px oklch(1 0 0 / 0.1), 0 1px 2px oklch(0 0 0 / 0.3);\n --shadow-card:\n 0 0 0 1px oklch(1 0 0 / 0.11), 0 1px 2px oklch(0 0 0 / 0.2),\n 0 2px 6px oklch(0 0 0 / 0.2);\n --shadow-raised:\n 0 0 0 1px oklch(1 0 0 / 0.13), 0 2px 10px oklch(0 0 0 / 0.22);\n --shadow-overlay:\n 0 0 0 1px oklch(1 0 0 / 0.15), 0 8px 28px oklch(0 0 0 / 0.34);\n --shadow-inset-field: inset 0 1px 2px oklch(0 0 0 / 0.4);\n}\n\n@theme inline {\n /* colors */\n --color-page: var(--page);\n --color-canvas: var(--canvas);\n --color-surface: var(--surface);\n --color-inset: var(--inset);\n --color-ink: var(--ink);\n --color-ink-2: var(--ink-2);\n --color-ink-3: var(--ink-3);\n --color-hover: var(--hover);\n --color-hover-2: var(--hover-2);\n --color-field: var(--field);\n --color-line: var(--line);\n --color-line-strong: var(--line-strong);\n --color-accent: var(--accent);\n --color-accent-ink: var(--accent-ink);\n --color-accent-tint: var(--accent-tint);\n --color-green: var(--green);\n --color-green-tint: var(--green-tint);\n --color-orange: var(--orange);\n --color-orange-tint: var(--orange-tint);\n --color-red: var(--red);\n --color-red-tint: var(--red-tint);\n\n /* shadows */\n --shadow-hairline: var(--shadow-hairline);\n --shadow-btn: var(--shadow-btn);\n --shadow-inset-field: var(--shadow-inset-field);\n --shadow-card: var(--shadow-card);\n --shadow-raised: var(--shadow-raised);\n --shadow-overlay: var(--shadow-overlay);\n\n /* type */\n --font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;\n --font-mono: var(--font-mono-face), ui-monospace, \"SF Mono\", monospace;\n\n /* radii — chips 6, controls 8, cards 10, windows 14, pills 999 */\n --radius-chip: 6px;\n --radius-control: 8px;\n --radius-card: 10px;\n --radius-window: 14px;\n\n /* easing — strong curves; built-ins are too weak */\n --ease-out-strong: cubic-bezier(0.23, 1, 0.32, 1);\n --ease-in-out-strong: cubic-bezier(0.77, 0, 0.175, 1);\n --ease-link: cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n/* ── base ─────────────────────────────────────────────── */\n\nhtml {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n}\n\nbody {\n background-color: var(--stripe-bg);\n background-image: repeating-linear-gradient(\n -45deg,\n transparent 0,\n transparent 7px,\n var(--stripe) 7px,\n var(--stripe) 8px\n );\n /* Pin the stripe pattern to the viewport so it stays put on scroll. */\n background-attachment: fixed;\n color: var(--ink);\n font-feature-settings: \"cv11\", \"ss01\";\n font-size: 14px;\n letter-spacing: -0.01em;\n line-height: 1.5;\n}\n\n::selection {\n background: var(--accent-tint);\n color: var(--ink);\n}\n\n/* ── interface hygiene (interfaces.dev cheat sheet) ────── */\n\n/* headings balance; descriptions avoid orphans (skip long-form — we have none) */\nh1,\nh2,\nh3,\nh4 {\n text-wrap: balance;\n}\n\np {\n text-wrap: pretty;\n}\n\n/* underlines clear descenders */\na {\n text-underline-position: from-font;\n text-decoration-skip-ink: auto;\n}\n\n/* theme switches swap every token at once — freeze transitions so the flip\n * is one clean repaint instead of hundreds of mismatched color fades */\n.theme-switching *,\n.theme-switching *::before,\n.theme-switching *::after {\n transition: none !important;\n}\n\n/* touch: :hover sticks after a tap, so neutralize row/header hovers there */\n@media (hover: none) {\n .records-row:hover > .records-cell {\n background: transparent;\n }\n\n .records-row:hover > td.is-colsel {\n background: color-mix(in srgb, var(--accent) 4%, var(--surface));\n }\n\n .records-header-button:hover {\n background: transparent;\n color: var(--ink-2);\n }\n}\n\n/* Keep the component nav scrollable but hide its scrollbar. */\n.component-nav-scroll {\n scrollbar-width: none; /* Firefox */\n -ms-overflow-style: none; /* legacy Edge */\n}\n.component-nav-scroll::-webkit-scrollbar {\n display: none; /* WebKit */\n}\n\n@media (min-width: 64rem) {\n .component-nav-scroll {\n -webkit-mask-image: linear-gradient(\n to bottom,\n oklch(0 0 0) 0,\n oklch(0 0 0) calc(100% - 6rem),\n transparent 100%\n );\n mask-image: linear-gradient(\n to bottom,\n oklch(0 0 0) 0,\n oklch(0 0 0) calc(100% - 6rem),\n transparent 100%\n );\n }\n}\n\n/* Mobile demo gestures should operate controls and visualizations rather than\n * selecting surrounding labels. Text entry and code remain intentionally\n * selectable. */\n@media (hover: none) and (pointer: coarse) {\n .primitive-showcase {\n -webkit-user-select: none;\n user-select: none;\n -webkit-touch-callout: none;\n }\n\n .primitive-showcase :is(input, textarea, [contenteditable=\"true\"], pre, code) {\n -webkit-user-select: text;\n user-select: text;\n -webkit-touch-callout: default;\n }\n\n .primitive-showcase :is(a, button, [role=\"button\"]) {\n -webkit-tap-highlight-color: transparent;\n }\n\n .primitive-showcase :is(a, button, [role=\"button\"]):focus-visible {\n outline: none;\n }\n\n .primitive-showcase :is(img, svg, canvas) {\n -webkit-user-drag: none;\n }\n\n .insight-chart-stage {\n touch-action: pan-y;\n }\n}\n\n/* Tailwind v4 resets buttons to cursor:default — anything\n * clickable must invite the click */\nbutton:not(:disabled),\n[role=\"button\"]:not([aria-disabled=\"true\"]),\nlabel:has(input),\na {\n cursor: pointer;\n}\n\n\n:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n border-radius: 4px;\n}\n\ninput:focus-visible,\ntextarea:focus-visible,\nselect:focus-visible,\n[contenteditable]:focus-visible {\n outline: none;\n}\n\n.animated-underline {\n position: relative;\n display: inline-block;\n}\n\n.animated-underline::after {\n position: absolute;\n right: 0;\n bottom: -1px;\n left: 0;\n height: 1px;\n content: \"\";\n background: currentColor;\n transform: scaleX(0);\n transform-origin: left center;\n transition: transform 280ms var(--ease-link);\n}\n\na:hover .animated-underline::after,\na:focus-visible .animated-underline::after {\n transform: scaleX(1);\n}\n\n/* ══════════════════════════════════════════════════════════\n * COMPONENT-SPECIFIC — StreamingText / ContextCards\n * Favicon source avatars.\n * Copy this block only alongside StreamingText / ContextCards; it is not part of the\n * generic foundation above.\n * ══════════════════════════════════════════════════════════ */\n.source-avatar {\n box-shadow: 0 0 0 1px oklch(0.21 0.034 263.436 / 0.1);\n}\n\n.dark .source-avatar {\n box-shadow: 0 0 0 1px oklch(1 0 0 / 0.08);\n}\n\n/* ── primitive spacing system ─────────────────────────────\n * Shared optical offsets for small demo cards. Use these\n * instead of hand-tuning 10/12/14px per component.\n */\n\n.primitive-card-pad {\n padding: 12px;\n}\n\n.primitive-card-bar {\n padding: 10px 12px;\n}\n\n.primitive-card-footer {\n padding: 10px;\n}\n\n.primitive-table-cell {\n padding: 10px 12px;\n}\n\n.primitive-icon-button {\n display: inline-flex;\n width: 28px;\n height: 28px;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-control);\n}\n\n/* ── shared keyframes ─────────────────────────────────── */\n\n@keyframes shimmer-text {\n from {\n background-position: 150% center;\n }\n to {\n background-position: -50% center;\n }\n}\n\n@keyframes fade-up {\n from {\n opacity: 0;\n transform: translateY(8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.home-reveal {\n will-change: opacity, transform, filter;\n}\n\n/* The sidebar is one persistent 224px tree clipped by a 52px shell.\n * Every rail glyph remains centered at x=26 in both states. */\n .records-footer-hint {\n display: none;\n }\n}\n\n/* ── filter table electric status colors ─────────────────── */\n\n/* ══════════════════════════════════════════════════════════\n * COMPONENT-SPECIFIC — FilterTable\n * Electric status-pill colors.\n * Copy this block only alongside FilterTable; it is not part of the\n * generic foundation above.\n * ══════════════════════════════════════════════════════════ */\n.filter-status-todo,\n.filter-status-progress,\n.filter-status-done {\n border: 1px solid transparent;\n}\n\n/* ══════════════════════════════════════════════════════════\n * COMPONENT-SPECIFIC — InsightCards\n * Liveline chart tooltip + cursor.\n * Copy this block only alongside InsightCards; it is not part of the\n * generic foundation above.\n * ══════════════════════════════════════════════════════════ */\n.insight-chart-stage {\n overflow: hidden;\n}\n\n.insight-chart-cursor {\n position: absolute;\n z-index: 4;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--ink);\n opacity: 0.26;\n pointer-events: none;\n}\n\n.insight-chart-tooltip-anchor {\n position: absolute;\n z-index: 5;\n top: 8px;\n transform: translateX(-50%);\n pointer-events: none;\n}\n\n.insight-chart-tooltip {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 5px 9px;\n border: 1px solid var(--line-strong);\n border-radius: 8px;\n color: var(--tooltip-fg);\n background: var(--tooltip-bg);\n box-shadow: var(--shadow-overlay);\n font-size: 11.5px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.insight-chart-tooltip-item {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.insight-chart-tooltip-dot {\n width: 8px;\n height: 8px;\n flex: 0 0 8px;\n border-radius: 50%;\n}\n\n/* Status pills share the same base-hue → color-mix derivation as .records-tag,\n * so both components stay consistent across light and dark. */\n.filter-status-todo {\n --tag-base: oklch(0.757 0.153 66.401);\n}\n.filter-status-progress {\n --tag-base: oklch(0.671 0.118 219.351);\n}\n.filter-status-done {\n --tag-base: oklch(0.652 0.131 162.865);\n}\n\n.filter-status-todo,\n.filter-status-progress,\n.filter-status-done {\n color: color-mix(in srgb, var(--tag-base) 92%, var(--ink));\n background: color-mix(in srgb, var(--tag-base) 20%, var(--surface));\n border-color: color-mix(in srgb, var(--tag-base) 34%, var(--surface));\n}\n\n/* Dark mode — a richer, saturated same-hue fill\n * with bright same-hue text and no visible border (light mode keeps the soft\n * tinted pastels above). Applies to both the records grid and status pills. */\n.dark .records-tag,\n.dark .filter-status-todo,\n.dark .filter-status-progress,\n.dark .filter-status-done {\n color: color-mix(in srgb, var(--tag-base) 86%, white);\n background: color-mix(in srgb, var(--tag-base) 34%, var(--surface));\n border-color: color-mix(in srgb, var(--tag-base) 34%, var(--surface));\n}\n\n@keyframes fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n@keyframes eq-bounce {\n 0%,\n 100% {\n transform: scaleY(0.35);\n }\n 50% {\n transform: scaleY(1);\n }\n}\n\n/* ── StreamText primitive ─────────────────────────────────\n * Fast character reveal: the newest few characters carry a\n * soft, masked blur so the leading edge resolves smoothly as\n * text flows in. The caret holds solid while streaming, then\n * blinks once the string settles. */\n/* ══════════════════════════════════════════════════════════\n * COMPONENT-SPECIFIC — StreamText\n * The streaming caret + tail.\n * Copy this block only alongside StreamText; it is not part of the\n * generic foundation above.\n * ══════════════════════════════════════════════════════════ */\n.stream-tail {\n filter: blur(1.6px);\n -webkit-mask-image: linear-gradient(to right, oklch(0 0 0) 20%, oklch(0 0 0 / 0.2));\n mask-image: linear-gradient(to right, oklch(0 0 0) 20%, oklch(0 0 0 / 0.2));\n}\n\n.stream-caret {\n display: inline-block;\n width: 2px;\n height: 1.05em;\n margin-left: 1.5px;\n translate: 0 -0.5px;\n border-radius: 1px;\n background: var(--ink);\n vertical-align: text-bottom;\n animation: caret-blink 1s step-end infinite;\n}\n\n.stream-caret.is-streaming {\n animation: none;\n}\n\n@keyframes caret-blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .stream-caret {\n animation: none;\n }\n .stream-tail {\n filter: none;\n -webkit-mask-image: none;\n mask-image: none;\n }\n}\n\n@keyframes pop-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* pixel-grid loader — brighten plateau, staggered per cell */\n@keyframes pixel-on {\n 0%, 100% {\n opacity: 0.15;\n }\n 18%, 42% {\n opacity: 1;\n }\n 62% {\n opacity: 0.15;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n",
|
|
"type": "registry:file",
|
|
"target": "app/beautifui/foundation.css"
|
|
}
|
|
]
|
|
} |