Files
pocket-id/frontend/src/app.css
T

648 lines
17 KiB
CSS

@import 'tailwindcss';
@import 'tw-animate-css';
@import 'shadcn-svelte/tailwind.css';
@import './fonts.css';
@variant dark (&:where(.dark, .dark *));
/* The values below are the Mono color theme; applyColorTheme in color-theme-util.ts overrides the neutrals and the primary family at runtime */
:root {
--h-xs: 26px;
--h-sm: 34px;
--h-md: 42px;
--h-lg: 48px;
--h-xl: 48px;
--row-dense: 36px;
--row: 44px;
--row-2line: 52px;
--col-auth: 464px;
--col-content: 1120px;
/* The identity column gives way on tablets so the other columns fit without scrolling sideways */
--table-id-max: clamp(240px, 36vw, 440px);
--sidebar-w: 256px;
--rail-w: 60px;
--dialog-sm: 420px;
--dialog-md: 480px;
--dialog-lg: 560px;
--sheet-w: 560px;
--palette-w: 640px;
--toast-w: 340px;
--clear-end-phone: 148px;
--z-sticky: 10;
--z-nav: 20;
--z-bar: 30;
--z-overlay: 40;
--z-popover: 50;
--z-toast: 60;
--z-tooltip: 70;
--z-progress: 80;
--halo-width: 3px;
--selection-border: 1.5px;
--dur-fast: 120ms;
--dur-base: 200ms;
--dur-slow: 280ms;
--rise: 4px;
--nudge: 2px;
--slide: 24px;
--press-scale: 0.98;
--pop-scale: 0.96;
--t-success-hold: 800ms;
--t-redirect-fallback: 3000ms;
--t-dim-delay: 200ms;
--t-skeleton-delay: 150ms;
--t-pulse: 1600ms;
--t-spin: 800ms;
--t-caret: 1000ms;
--t-progress: 3000ms;
--canvas: #ffffff;
--surface: #ffffff;
--surface-raised: #ffffff;
--surface-sunken: #f5f5f5;
--fill-muted: #f2f2f2;
--fill-hover: #eeeeee;
--fill-row-hover: #fafafa;
--fill-selected: #ebebeb;
--fill-skeleton: #eeeeee;
--border: #e8e8e8;
--border-strong: #8c8c8c;
--border-control: #8c8c8c;
--fg: #0a0a0a;
--fg-muted: #5e5e5e;
--fg-subtle: #a3a3a3;
--inverse: #111111;
--inverse-fg: #fafafa;
--inverse-muted: #b5b5b5;
--inverse-hover: #262626;
--inverse-border: transparent;
--success-solid: #319751;
--success-fg: #206b38;
--success-soft: #ebfaed;
--success-border: #bcdfc2;
--warning-solid: #c5770f;
--warning-fg: #864e18;
--warning-soft: #fff6e4;
--warning-border: #eecf9c;
--danger-solid: #d73337;
--danger-fg: #b32228;
--danger-soft: #fff1f0;
--danger-border: #f9c1bf;
--danger-fill: #c52b30;
--danger-fill-hover: #b32228;
--danger-fill-fg: #ffffff;
--info-solid: #5e5e5e;
--info-fg: #0a0a0a;
--info-soft: #f5f5f5;
--info-border: #e8e8e8;
--well-light: #f5f5f5;
--well-dark: #000000;
--well-light-fg: #5e5e5e;
--well-dark-fg: #a1a1a1;
--qr-bg: #ffffff;
--scrim: rgb(18 17 16 / 0.4);
--ring: #0a0a0a;
--ring-halo: rgb(10 10 10 / 0.24);
--elevation-popover: 0 1px 2px rgb(31 29 27 / 0.06), 0 8px 24px -6px rgb(31 29 27 / 0.14);
--elevation-overlay: 0 2px 4px rgb(31 29 27 / 0.06), 0 16px 40px -8px rgb(31 29 27 / 0.2);
--elevation-thumb: 0 1px 2px rgb(0 0 0 / 0.2);
--elevation-selected: 0 1px 2px rgb(31 29 27 / 0.06), 0 4px 14px rgb(31 29 27 / 0.07);
--border-selected: color-mix(in oklab, var(--border-strong) 40%, var(--border));
--sidebar: #fafafa;
--primary: #0a0a0a;
--primary-foreground: #ffffff;
--primary-hover: #262626;
--primary-control: #0a0a0a;
--primary-control-foreground: #ffffff;
--primary-edge: transparent;
--fw-display-l: 600;
--fw-display-m: 600;
/* shadcn-svelte variables, mapped onto the tokens above so the ui/ components keep their class names */
--background: var(--canvas);
--foreground: var(--fg);
--card: var(--surface);
--card-foreground: var(--fg);
--popover: var(--surface-raised);
--popover-foreground: var(--fg);
--secondary: var(--fill-muted);
--secondary-foreground: var(--fg);
--muted: var(--fill-muted);
--muted-foreground: var(--fg-muted);
--accent: var(--fill-hover);
--accent-foreground: var(--fg);
--destructive: var(--danger-fg);
--destructive-foreground: var(--danger-fill-fg);
--warning: var(--warning-soft);
--warning-foreground: var(--warning-fg);
--input: var(--fill-muted);
--sidebar-foreground: var(--fg);
--sidebar-primary: var(--primary);
--sidebar-primary-foreground: var(--primary-foreground);
--sidebar-accent: var(--fill-selected);
--sidebar-accent-foreground: var(--fg);
--sidebar-border: var(--border);
--sidebar-ring: var(--ring);
}
.dark {
--canvas: #000000;
--surface: #000000;
--surface-raised: #121212;
--surface-sunken: #0e0e0e;
--fill-muted: #171717;
--fill-hover: #1a1a1a;
--fill-row-hover: #0c0c0c;
--fill-selected: #1f1f1f;
--fill-skeleton: #1a1a1a;
--border: #1f1f1f;
--border-strong: #6b6b6b;
--border-control: #6b6b6b;
--fg: #ededed;
--fg-muted: #a1a1a1;
--fg-subtle: #5c5c5c;
--inverse: #1f1f1f;
--inverse-fg: #ededed;
--inverse-muted: #b5b5b5;
--inverse-hover: #2e2e2e;
--inverse-border: #333333;
--success-solid: #54b66e;
--success-fg: #83d494;
--success-soft: #1a2b1d;
--success-border: #2c5235;
--warning-solid: #eba941;
--warning-fg: #f5ca7a;
--warning-soft: #342611;
--warning-border: #68491a;
--danger-solid: #f45a56;
--danger-fg: #ff8f8d;
--danger-soft: #3b1e1e;
--danger-border: #813939;
--danger-fill: #c52b30;
--danger-fill-hover: #b32228;
--danger-fill-fg: #ffffff;
--info-solid: #a1a1a1;
--info-fg: #ededed;
--info-soft: #0e0e0e;
--info-border: #1f1f1f;
--well-light: #f5f5f5;
--well-dark: #000000;
--well-light-fg: #5e5e5e;
--well-dark-fg: #a1a1a1;
--qr-bg: #ffffff;
--scrim: rgb(0 0 0 / 0.6);
--ring: #ededed;
--ring-halo: rgb(237 237 237 / 0.24);
--elevation-popover: 0 1px 2px rgb(0 0 0 / 0.4), 0 8px 24px -6px rgb(0 0 0 / 0.6);
--elevation-overlay: 0 2px 4px rgb(0 0 0 / 0.4), 0 16px 40px -8px rgb(0 0 0 / 0.7);
--elevation-thumb: 0 1px 2px rgb(0 0 0 / 0.5);
--elevation-selected: 0 1px 2px rgb(0 0 0 / 0.4), 0 4px 14px rgb(0 0 0 / 0.5);
--sidebar: #0a0a0a;
--primary: #ededed;
--primary-foreground: #000000;
--primary-hover: #d6d6d6;
--primary-control: #ededed;
--primary-control-foreground: #000000;
--primary-edge: transparent;
--fw-display-l: 500;
--fw-display-m: 500;
color-scheme: dark;
}
/* Reduced motion: the OS setting and the Disable animations setting share one token collapse */
html.no-motion {
--dur-fast: 0ms;
--dur-base: 0ms;
--dur-slow: 0ms;
--rise: 0px;
--nudge: 0px;
--slide: 0px;
--press-scale: 1;
--pop-scale: 1;
--t-pulse: 0ms;
--t-caret: 0ms;
--t-progress: 0ms;
--t-spin: 1200ms;
}
@media (prefers-reduced-motion: reduce) {
:root {
--dur-fast: 0ms;
--dur-base: 0ms;
--dur-slow: 0ms;
--rise: 0px;
--nudge: 0px;
--slide: 0px;
--press-scale: 1;
--pop-scale: 1;
--t-pulse: 0ms;
--t-caret: 0ms;
--t-progress: 0ms;
--t-spin: 1200ms;
}
}
/* Safety net for motion that does not read the tokens (bits-ui keyframes, svelte-sonner): only spinners and scroll-linked fades keep moving */
html.no-motion *:not(.animate-spin, .scroll-fade-x, .scroll-fade-y),
html.no-motion *::before,
html.no-motion *::after {
animation-duration: 0s !important;
transition-duration: 0s !important;
}
@media (prefers-reduced-motion: reduce) {
*:not(.animate-spin, .scroll-fade-x, .scroll-fade-y),
*::before,
*::after {
animation-duration: 0s !important;
transition-duration: 0s !important;
}
}
/* Set for two frames by setModeInstantly, so light and dark swap at once instead of fading control by control */
html.theme-switching *,
html.theme-switching *::before,
html.theme-switching *::after {
transition: none !important;
}
@theme {
--font-sans: 'Funnel Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
--font-display: 'Funnel Display', 'Funnel Sans', ui-sans-serif, system-ui, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
--radius-xs: 6px;
--radius-sm: 10px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-2xl: 24px;
--radius-3xl: 28px;
--radius-4xl: 32px;
--ease-out: cubic-bezier(0.2, 0, 0, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
}
@theme inline {
--text-display-l: 34px;
--text-display-l--line-height: 42px;
--text-display-l--font-weight: var(--fw-display-l);
--text-display-l--letter-spacing: -0.02em;
--text-display-m: 28px;
--text-display-m--line-height: 36px;
--text-display-m--font-weight: var(--fw-display-m);
--text-display-m--letter-spacing: -0.015em;
--text-display-s: 22px;
--text-display-s--line-height: 30px;
--text-display-s--font-weight: 500;
--text-display-s--letter-spacing: -0.01em;
--text-title: 18px;
--text-title--line-height: 26px;
--text-title--font-weight: 600;
--text-title--letter-spacing: 0em;
--text-heading: 15px;
--text-heading--line-height: 22px;
--text-heading--font-weight: 600;
--text-heading--letter-spacing: 0em;
--text-body-l: 17px;
--text-body-l--line-height: 26px;
--text-body-l--font-weight: 400;
--text-body-l--letter-spacing: 0em;
--text-body: 15px;
--text-body--line-height: 22px;
--text-body--font-weight: 400;
--text-body--letter-spacing: 0em;
--text-control: 14px;
--text-control--line-height: 20px;
--text-control--font-weight: 400;
--text-control--letter-spacing: 0em;
--text-caption: 13px;
--text-caption--line-height: 18px;
--text-caption--font-weight: 400;
--text-caption--letter-spacing: 0em;
--text-micro: 12px;
--text-micro--line-height: 16px;
--text-micro--font-weight: 500;
--text-micro--letter-spacing: 0.01em;
--text-mono: 14px;
--text-mono--line-height: 20px;
--text-mono--font-weight: 400;
--text-mono--letter-spacing: 0em;
--text-mono-s: 13px;
--text-mono-s--line-height: 18px;
--text-mono-s--font-weight: 400;
--text-mono-s--letter-spacing: 0em;
--text-code-cell: 30px;
--text-code-cell--line-height: 38px;
--text-code-cell--font-weight: 500;
--text-code-cell--letter-spacing: 0em;
--shadow-popover: var(--elevation-popover);
--shadow-overlay: var(--elevation-overlay);
--shadow-thumb: var(--elevation-thumb);
--shadow-selected: var(--elevation-selected);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
--color-input: var(--input);
--color-ring: var(--ring);
--color-ring-halo: var(--ring-halo);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--color-scrim: var(--scrim);
--color-canvas: var(--canvas);
--color-surface: var(--surface);
--color-surface-raised: var(--surface-raised);
--color-surface-sunken: var(--surface-sunken);
--color-fill-muted: var(--fill-muted);
--color-fill-hover: var(--fill-hover);
--color-fill-row-hover: var(--fill-row-hover);
--color-fill-selected: var(--fill-selected);
--color-fill-skeleton: var(--fill-skeleton);
--color-border: var(--border);
--color-border-strong: var(--border-strong);
--color-border-selected: var(--border-selected);
--color-fg: var(--fg);
--color-fg-muted: var(--fg-muted);
--color-fg-subtle: var(--fg-subtle);
--color-inverse: var(--inverse);
--color-inverse-fg: var(--inverse-fg);
--color-inverse-muted: var(--inverse-muted);
--color-inverse-hover: var(--inverse-hover);
--color-inverse-border: var(--inverse-border);
--color-success-solid: var(--success-solid);
--color-success-soft: var(--success-soft);
--color-warning-solid: var(--warning-solid);
--color-warning-fg: var(--warning-fg);
--color-danger-solid: var(--danger-solid);
--color-danger-fg: var(--danger-fg);
--color-danger-soft: var(--danger-soft);
--color-danger-fill: var(--danger-fill);
--color-danger-fill-hover: var(--danger-fill-hover);
--color-danger-fill-fg: var(--danger-fill-fg);
--color-well-light: var(--well-light);
--color-well-dark: var(--well-dark);
--color-well-light-fg: var(--well-light-fg);
--color-well-dark-fg: var(--well-dark-fg);
--color-qr-bg: var(--qr-bg);
--color-sidebar: var(--sidebar);
--color-primary-hover: var(--primary-hover);
--color-primary-control: var(--primary-control);
--color-primary-control-foreground: var(--primary-control-foreground);
}
@layer base {
* {
@apply border-border outline-ring;
}
/* Links and anything else without its own focus style get the standard ring, which component utilities override */
:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
html {
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
/* Names are interpolated into sentences, so a long unbroken one wraps instead of pushing the page sideways */
body {
@apply bg-canvas text-body text-fg wrap-break-word;
}
button {
@apply cursor-pointer;
}
/* Text selection stays neutral: the accent marks state, never decoration */
::selection {
background: var(--fill-selected);
color: var(--fg);
}
/* Lucide icons keep a constant stroke regardless of their rendered size */
svg.lucide {
stroke-width: 1.5px;
}
svg.lucide * {
vector-effect: non-scaling-stroke;
}
}
/* Compositor-only motion; Tailwind 4 writes scale and translate as their own properties, so they are listed next to transform */
@utility transition-motion {
transition-property: opacity, transform, translate, scale;
transition-duration: var(--dur-fast);
transition-timing-function: var(--ease-out);
}
.animate-fade-in {
animation: fadeIn 0.3s ease-out forwards;
opacity: 0;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.settings-content {
view-transition-name: settings-content;
}
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
}
::view-transition-group(settings-content) {
animation: none;
}
::view-transition-old(settings-content) {
animation: 120ms ease-out both vt-fade-out;
}
::view-transition-new(settings-content) {
animation: 180ms ease-out both vt-fade-in;
}
@keyframes vt-fade-out {
to {
opacity: 0;
}
}
@keyframes vt-fade-in {
from {
opacity: 0;
}
}
@keyframes bg-zoom {
0% {
transform: scale(1.3);
}
100% {
transform: scale(1);
}
}
.animate-bg-zoom {
transform-origin: center;
will-change: transform;
animation: bg-zoom 0.7s cubic-bezier(0.25, 0.1, 0.25, 1) forwards;
}
@keyframes delayed-fade {
0%,
40% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.animate-delayed-fade {
animation: delayed-fade 0.5s ease-out forwards;
}
@keyframes shake {
0%,
100% {
transform: translateX(0);
}
25% {
transform: translateX(-10px);
}
50% {
transform: translateX(10px);
}
75% {
transform: translateX(-10px);
}
}
.animate-shake {
animation: shake 0.5s ease-in-out;
}
/* The bar shares its surface token with the cards it floats over, so it needs an elevation of its own */
.shadow-raised {
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.5),
0 1px 2px rgb(0 0 0 / 0.08),
0 12px 32px -10px rgb(0 0 0 / 0.25);
}
.dark .shadow-raised {
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.07),
0 1px 2px rgb(0 0 0 / 0.6),
0 16px 40px -12px rgb(0 0 0 / 0.9);
}
/* Marks a moment: the bar arriving, a save settling, a blocked navigation */
@keyframes bloom {
0% {
opacity: 0;
}
30% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.animate-bloom {
animation: bloom 1.5s ease-out forwards;
}
/* Dark mode has glow headroom: a luminous colour over a near-black pill reads as light spilling out of it */
/* Light mode has none, so the same gradient washes out to almost nothing there */
/* The light values keep the same origin below the pill, so the bloom rises the same way in both, but spread wider and carry more colour to register against white */
.bloom-pending {
background: radial-gradient(150% 260% at 50% 140%, rgb(217 119 6 / 0.26), transparent 72%);
}
/* Waits out the bar's 380ms motion so it blooms after the pill settles */
/* `both` keeps it hidden during that wait */
.bloom-success {
background: radial-gradient(150% 260% at 50% 140%, rgb(22 163 74 / 0.3), transparent 72%);
animation-delay: 380ms;
animation-fill-mode: both;
}
.bloom-error {
background: radial-gradient(
150% 260% at 50% 140%,
color-mix(in oklab, var(--destructive) 28%, transparent),
transparent 72%
);
}
.dark .bloom-pending {
background: radial-gradient(130% 150% at 50% 130%, rgb(251 191 36 / 0.24), transparent 70%);
}
.dark .bloom-success {
background: radial-gradient(130% 150% at 50% 130%, rgb(34 197 94 / 0.24), transparent 70%);
}
.dark .bloom-error {
background: radial-gradient(
130% 150% at 50% 130%,
color-mix(in oklab, var(--destructive) 26%, transparent),
transparent 70%
);
}
@media (prefers-reduced-motion: reduce) {
.animate-bloom {
animation: none;
}
}