Files
CurrenciCombo/src/styles/tokens.css
Devin AI 46cce8f44e Responsive UX/UI system: tokens, hooks, drawer nav, workspace gate, a11y primitives
Adds a CSS-first responsive foundation with breakpoint tokens, fluid
typography/spacing via clamp(), and matchMedia-driven hooks. Portal
chrome swaps to an off-canvas drawer below md; workspace (IDE) shows
a friendly mobile gate below md with links to portal routes.

Details in docs/ux-responsive-strategy.md.

Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
2026-04-23 04:49:14 +00:00

133 lines
5.1 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ═══════════════════════════════════════════════════════════════
Design tokens — responsive foundation
Loaded BEFORE index.css so existing rules can consume these
variables. Strictly additive: no variable declared here conflicts
with an existing value in index.css.
Breakpoint policy (mobile-first, min-width):
xs: 0 479px (narrow phones, portrait)
sm: 480 767px (large phones, small tablets portrait)
md: 768 1023px (tablets landscape, small laptops)
lg: 1024 1439px (desktops, larger laptops)
xl: 1440px+ (wide desktops, ultra-wide)
We prefer container/fluid behavior; breakpoints gate only layout
switches that cannot be expressed fluidly (nav drawer, workspace
availability, table→card transform).
═══════════════════════════════════════════════════════════════ */
:root {
/* Breakpoints (CSS custom props mirror the JS hook constants) */
--bp-xs: 0px;
--bp-sm: 480px;
--bp-md: 768px;
--bp-lg: 1024px;
--bp-xl: 1440px;
/* Fluid type scale — clamp(min, preferred, max)
Preferred uses a viewport-width linear function so text grows
smoothly between xs and xl without hard breakpoint jumps.
All values are in rem so user font-size preferences are honored. */
--fs-2xs: clamp(0.625rem, 0.59rem + 0.17vw, 0.75rem); /* 10→12px */
--fs-xs: clamp(0.6875rem, 0.65rem + 0.19vw, 0.8125rem); /* 11→13px */
--fs-sm: clamp(0.75rem, 0.71rem + 0.21vw, 0.875rem); /* 12→14px */
--fs-base: clamp(0.8125rem, 0.77rem + 0.23vw, 1rem); /* 13→16px */
--fs-md: clamp(0.875rem, 0.83rem + 0.25vw, 1.125rem); /* 14→18px */
--fs-lg: clamp(1rem, 0.93rem + 0.38vw, 1.25rem); /* 16→20px */
--fs-xl: clamp(1.125rem, 1.0rem + 0.63vw, 1.5rem); /* 18→24px */
--fs-2xl: clamp(1.25rem, 1.04rem + 1.04vw, 1.875rem); /* 20→30px */
--fs-3xl: clamp(1.5rem, 1.19rem + 1.56vw, 2.25rem); /* 24→36px */
--fs-4xl: clamp(1.875rem, 1.35rem + 2.60vw, 3rem); /* 30→48px */
/* Line heights */
--lh-tight: 1.2;
--lh-snug: 1.35;
--lh-normal: 1.5;
--lh-relaxed: 1.65;
/* Fluid spacing scale (8pt grid, fluid from xs to xl) */
--space-0: 0;
--space-1: clamp(0.125rem, 0.11rem + 0.05vw, 0.1875rem); /* 2→3px */
--space-2: clamp(0.25rem, 0.22rem + 0.10vw, 0.375rem); /* 4→6px */
--space-3: clamp(0.375rem, 0.33rem + 0.17vw, 0.5rem); /* 6→8px */
--space-4: clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem); /* 8→12px */
--space-5: clamp(0.75rem, 0.65rem + 0.42vw, 1rem); /* 12→16px */
--space-6: clamp(1rem, 0.87rem + 0.56vw, 1.5rem); /* 16→24px */
--space-7: clamp(1.25rem, 1.04rem + 0.83vw, 2rem); /* 20→32px */
--space-8: clamp(1.5rem, 1.22rem + 1.04vw, 2.5rem); /* 24→40px */
--space-10: clamp(2rem, 1.65rem + 1.46vw, 3.5rem); /* 32→56px */
--space-12: clamp(2.5rem, 2.00rem + 2.08vw, 4.5rem); /* 40→72px */
/* Touch target minimum (WCAG 2.5.5 AA is 44×44 CSS px) */
--tap-min: 44px;
/* Container widths */
--container-sm: 640px;
--container-md: 768px;
--container-lg: 1024px;
--container-xl: 1280px;
--container-2xl: 1536px;
/* Motion tokens */
--motion-fast: 120ms;
--motion-base: 200ms;
--motion-slow: 320ms;
--motion-ease: cubic-bezier(0.4, 0, 0.2, 1);
/* Z-index scale */
--z-base: 0;
--z-sticky: 10;
--z-drawer-backdrop: 40;
--z-drawer: 50;
--z-dropdown: 60;
--z-modal: 100;
--z-toast: 200;
--z-tooltip: 300;
--z-focus: 999;
/* Focus ring */
--focus-ring-color: #60a5fa; /* light blue, visible on dark bg */
--focus-ring-offset: 2px;
--focus-ring-width: 2px;
/* Safe area insets (iOS notch, Android gesture areas) */
--safe-top: env(safe-area-inset-top, 0px);
--safe-right: env(safe-area-inset-right, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);
--safe-left: env(safe-area-inset-left, 0px);
}
/* Honor OS-level reduced motion — apply across the entire app.
Anything that relies on animation for state feedback (toasts,
drawer slide, spinner) must still convey state without motion.
Spinners use opacity pulses under the same media query. */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
}
/* Honor OS-level contrast preference */
@media (prefers-contrast: more) {
:root {
--focus-ring-width: 3px;
--focus-ring-color: #ffffff;
}
}
/* High-DPI tuning: tighten 1px borders on >= 2x DPR so they read as
hairlines and don't visually bloom. */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
:root {
--hairline: 0.5px;
}
}
@media not all and (-webkit-min-device-pixel-ratio: 2), not all and (min-resolution: 2dppx) {
:root {
--hairline: 1px;
}
}