diff --git a/src/App.tsx b/src/App.tsx index bb582ca..5e36c7a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -238,6 +238,7 @@ const App = () => {
Slider & stepper
+ setCount((n) => Math.max(0, n - 1))} onIncrement={() => setCount((n) => n + 1)} diff --git a/src/components/slider/index.tsx b/src/components/slider/index.tsx index ce4d6e4..4cba023 100644 --- a/src/components/slider/index.tsx +++ b/src/components/slider/index.tsx @@ -1,59 +1,125 @@ -import { type ComponentPropsWithoutRef } from 'react'; +import { type ComponentPropsWithoutRef, type CSSProperties } from 'react'; import { Slider as RSlider } from 'radix-ui'; -type Step = { value: number; label?: string }; +type Mark = { value: number; label?: string }; +type MarksProp = boolean | Array; +type NotchPlacement = 'top' | 'bottom' | 'both' | 'none'; -type SliderProps = ComponentPropsWithoutRef & { - steps?: number | Step[]; +type KnobStyle = 'square' | 'round'; + +type SliderProps = Omit, 'className'> & { + /** + * Step marks. + * - `true` — auto-generate one mark per `step` between `min` and `max`. + * - array — explicit marks; numbers or `{ value, label }` for tick labels. + */ + marks?: MarksProp; + /** + * Where to draw the notch ticks relative to the track. + * `'bottom'` (default), `'top'`, `'both'`, or `'none'` to hide ticks + * (labels still render when provided). No effect without `marks`. + */ + notches?: NotchPlacement; + /** Thumb shape. `'square'` (default) has a small border-radius; `'round'` is a full circle. */ + knobStyle?: KnobStyle; + className?: string; }; -function resolveSteps(steps: number | Step[], min: number, max: number): Step[] { - if (Array.isArray(steps)) return steps; - if (steps < 2) return []; - return Array.from({ length: steps }, (_, i) => ({ - value: min + (i / (steps - 1)) * (max - min), - })); +function resolveMarks( + marks: MarksProp, + min: number, + max: number, + step: number, +): Mark[] { + if (Array.isArray(marks)) { + return marks + .map((m) => (typeof m === 'number' ? { value: m } : m)) + .filter((m) => m.value >= min && m.value <= max); + } + if (marks !== true) return []; + if (!(step > 0) || max <= min) return []; + const count = Math.floor((max - min) / step); + // Guard against absurd notch counts (e.g. step=1 over a 0–1000 range). + if (count < 1 || count > 100) return []; + return Array.from({ length: count + 1 }, (_, i) => ({ value: min + i * step })); } -export const Slider = ({ steps, min = 0, max = 100, ...props }: SliderProps) => { - const resolved = steps != null ? resolveSteps(steps, min, max) : []; - const hasSteps = resolved.length > 0; +const percent = (value: number, min: number, max: number) => + max === min ? 0 : (value - min) / (max - min); + +const NotchLayer = ({ + marks, + min, + max, + side, +}: { + marks: Mark[]; + min: number; + max: number; + side: 'top' | 'bottom'; +}) => ( +
+ {marks.map((mark) => ( + + ))} +
+); + +export const Slider = ({ + marks, + notches = 'bottom', + knobStyle = 'square', + min = 0, + max = 100, + step = 1, + className, + ...props +}: SliderProps) => { + const resolved = marks != null ? resolveMarks(marks, min, max, step) : []; + const hasMarks = resolved.length > 0; + const hasLabels = resolved.some((m) => m.label != null); + const showTop = hasMarks && (notches === 'top' || notches === 'both'); + const showBottom = hasMarks && (notches === 'bottom' || notches === 'both'); + + const cls = [ + 'modern-sk-slider', + `modern-sk-slider--knob-${knobStyle}`, + hasMarks && 'modern-sk-slider--has-marks', + hasLabels && 'modern-sk-slider--has-labels', + showTop && 'modern-sk-slider--notch-top', + showBottom && 'modern-sk-slider--notch-bottom', + className, + ] + .filter(Boolean) + .join(' '); return ( - + - {hasSteps && resolved.map((step) => ( -
- ))} + {showTop && } + {showBottom && } + {hasLabels && ( +
+ {resolved.map((mark) => + mark.label != null ? ( + + {mark.label} + + ) : null, + )} +
+ )} - {hasSteps && ( -
- {resolved.map((step) => ( -
-
- {step.label != null && ( - {step.label} - )} -
- ))} -
- )} ); }; diff --git a/src/components/spinner/index.tsx b/src/components/spinner/index.tsx index 684fb6f..05d7003 100644 --- a/src/components/spinner/index.tsx +++ b/src/components/spinner/index.tsx @@ -6,7 +6,9 @@ export const Spinner = ({ className, ...props }: ComponentPropsWithoutRef<'span'> & { size?: 'sm' | 'lg' }) => { - const gid = `modern-sk-groove-${useId()}`; + const uid = useId(); + const grooveId = `modern-sk-groove-${uid}`; + const glowId = `modern-sk-glow-${uid}`; return ( - - - - + {/* Carved channel: flat ring sunk by a top inner shadow — like the switch well. */} + + + + + + + + + + + + + + + {/* Soft round glow — generous region so it never clips to a square. */} + + + - + + + + ); diff --git a/src/stories/Button.stories.tsx b/src/stories/Button.stories.tsx index 16c8cf9..feb029a 100644 --- a/src/stories/Button.stories.tsx +++ b/src/stories/Button.stories.tsx @@ -26,6 +26,7 @@ const meta = { iconOnly: { control: 'boolean', description: 'Square padding for a single glyph.' }, disabled: { control: 'boolean' }, children: { control: 'text' }, + className: { control: 'text' }, }, args: { children: 'Button', variant: 'key' }, } satisfies Meta; diff --git a/src/stories/DataDisplay.stories.tsx b/src/stories/DataDisplay.stories.tsx index 1ea8ca5..57d9c33 100644 --- a/src/stories/DataDisplay.stories.tsx +++ b/src/stories/DataDisplay.stories.tsx @@ -10,6 +10,7 @@ import { Th, Td, Badge, + Chip, } from '../components/ui'; const meta = { @@ -18,18 +19,23 @@ const meta = { parameters: { docs: { description: { - component: 'Cards, selectable lists/rows, and the bordered table.', + component: 'Cards, selectable lists/rows, badges, chips, and the bordered table.', }, }, }, + argTypes: { + className: { control: 'text' }, + }, } satisfies Meta; export default meta; type Story = StoryObj; -export const CardSurface: Story = { - render: () => ( - +export const CardPlayground: Story = { + name: 'Card', + args: { children: 'Card content' }, + render: (args) => ( +

Storage

128 GB of 256 GB used.

@@ -47,6 +53,27 @@ export const ListRows: Story = { ), }; +export const BadgeShowcase: Story = { + render: () => ( +
+ Lime + Ember + Neutral + Outline + Online +
+ ), +}; + +export const Chips: Story = { + render: () => ( +
+ Design + {}}>Removable +
+ ), +}; + export const DataTable: Story = { render: () => ( diff --git a/src/stories/Feedback.stories.tsx b/src/stories/Feedback.stories.tsx index 97fbea5..076b3fa 100644 --- a/src/stories/Feedback.stories.tsx +++ b/src/stories/Feedback.stories.tsx @@ -14,11 +14,24 @@ const meta = { }, }, }, + argTypes: { + value: { control: { type: 'range', min: 0, max: 100, step: 1 } }, + className: { control: 'text' }, + }, + args: { value: 40 }, } satisfies Meta; export default meta; type Story = StoryObj; +export const Playground: Story = { + render: (args) => ( +
+ +
+ ), +}; + function ProgressDemo() { const [v, setV] = useState(40); return ( diff --git a/src/stories/IconButton.stories.tsx b/src/stories/IconButton.stories.tsx index 8fd4ac1..06f806f 100644 --- a/src/stories/IconButton.stories.tsx +++ b/src/stories/IconButton.stories.tsx @@ -14,8 +14,17 @@ const meta = { }, }, argTypes: { - variant: { control: 'inline-radio', options: ['key', 'primary', 'ember', 'ghost'] }, - size: { control: 'inline-radio', options: ['sm', undefined, 'lg'] }, + variant: { + control: 'inline-radio', + options: ['key', 'primary', 'ember', 'ghost'], + description: 'Visual emphasis. `key` is the default neutral button.', + }, + size: { + control: 'inline-radio', + options: ['sm', undefined, 'lg'], + description: 'Button size: `sm` compact, default regular, `lg` large.', + }, + disabled: { control: 'boolean' }, }, } satisfies Meta; diff --git a/src/stories/Overlays.stories.tsx b/src/stories/Overlays.stories.tsx index cbd15d2..60a4388 100644 --- a/src/stories/Overlays.stories.tsx +++ b/src/stories/Overlays.stories.tsx @@ -25,17 +25,32 @@ const meta = { }, argTypes: { children: { control: false }, - content: { control: false }, + content: { control: 'text' }, + delayDuration: { control: 'number' }, + open: { control: 'boolean' }, + defaultOpen: { control: 'boolean' }, + onOpenChange: { action: 'open changed' }, + sideOffset: { control: 'number' }, }, args: { - content: '', + content: 'Tooltip text', children: null, + delayDuration: 0, }, } satisfies Meta; export default meta; type Story = StoryObj; +export const Playground: Story = { + name: 'Tooltip Playground', + render: (args) => ( + + + + ), +}; + export const TooltipStory: Story = { name: 'Tooltip', render: () => ( diff --git a/src/stories/ScrollArea.stories.tsx b/src/stories/ScrollArea.stories.tsx new file mode 100644 index 0000000..a2277c7 --- /dev/null +++ b/src/stories/ScrollArea.stories.tsx @@ -0,0 +1,73 @@ +import type { Meta, StoryObj } from 'storybook-react-rsbuild'; +import { ScrollArea } from '../components/ui'; + +const meta = { + title: 'Layout/ScrollArea', + component: ScrollArea, + parameters: { + docs: { + description: { + component: + 'Radix ScrollArea with custom styled scrollbars. Wraps content in a viewport with vertical and horizontal scrollbars.', + }, + }, + }, + argTypes: { + children: { control: false }, + className: { control: 'text' }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Playground: Story = { + render: () => ( + +
+

Scrollable content

+ {Array.from({ length: 20 }).map((_, i) => ( +

+ Item {i + 1}: Lorem ipsum dolor sit amet, consectetur adipiscing elit. +

+ ))} +
+
+ ), +}; + +export const Vertical: Story = { + render: () => ( + +
+ {Array.from({ length: 30 }).map((_, i) => ( +
+ Row {i + 1} +
+ ))} +
+
+ ), +}; + +export const Horizontal: Story = { + render: () => ( + +
+ {Array.from({ length: 20 }).map((_, i) => ( +
+ Item {i + 1} +
+ ))} +
+
+ ), +}; diff --git a/src/stories/SegmentedControl.stories.tsx b/src/stories/SegmentedControl.stories.tsx new file mode 100644 index 0000000..6559324 --- /dev/null +++ b/src/stories/SegmentedControl.stories.tsx @@ -0,0 +1,68 @@ +import { useState } from 'react'; +import type { Meta, StoryObj } from 'storybook-react-rsbuild'; +import { SegmentedControl } from '../components/ui'; + +const meta = { + title: 'Selection/SegmentedControl', + component: SegmentedControl, + parameters: { + docs: { + description: { + component: + 'Single-select button group with animated thumb. Pass `items` array of `{ value, label }` objects, plus `value` and `onValueChange` for control.', + }, + }, + }, + argTypes: { + value: { control: 'text' }, + onValueChange: { action: 'value changed' }, + items: { control: false }, + className: { control: 'text' }, + disabled: { control: 'boolean' }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +function SegmentedDemo() { + const [v, setV] = useState('day'); + return ( + + ); +} + +export const Playground: Story = { + render: () => , +}; + +export const TimeRange: Story = { + render: () => , +}; + +function OptionsDemo() { + const [v, setV] = useState('draft'); + return ( + + ); +} + +export const Options: Story = { + render: () => , +}; diff --git a/src/stories/Select.stories.tsx b/src/stories/Select.stories.tsx index 9e3566f..4b1cc80 100644 --- a/src/stories/Select.stories.tsx +++ b/src/stories/Select.stories.tsx @@ -19,6 +19,15 @@ const meta = { }, }, }, + argTypes: { + items: { control: false }, + placeholder: { control: 'text' }, + disabled: { control: 'boolean' }, + defaultValue: { control: 'text' }, + value: { control: 'text' }, + onValueChange: { action: 'value changed' }, + 'aria-label': { control: 'text' }, + }, args: { items, placeholder: 'Pick a release…', 'aria-label': 'macOS release' }, } satisfies Meta; diff --git a/src/stories/Selection.stories.tsx b/src/stories/Selection.stories.tsx index fe77632..b7aaf48 100644 --- a/src/stories/Selection.stories.tsx +++ b/src/stories/Selection.stories.tsx @@ -22,11 +22,21 @@ const meta = { }, }, }, + argTypes: { + defaultChecked: { control: 'boolean' }, + checked: { control: 'boolean' }, + disabled: { control: 'boolean' }, + onCheckedChange: { action: 'checked changed' }, + }, } satisfies Meta; export default meta; type Story = StoryObj; +export const Playground: Story = { + args: { defaultChecked: false }, +}; + export const Switches: Story = { render: () => (
diff --git a/src/stories/Slider.stories.tsx b/src/stories/Slider.stories.tsx index 5a4fe26..03719dc 100644 --- a/src/stories/Slider.stories.tsx +++ b/src/stories/Slider.stories.tsx @@ -8,16 +8,67 @@ const meta = { docs: { description: { component: - 'Radix Slider in the carved-track skin. All Radix Slider props pass through (`defaultValue`, `min`, `max`, `step`, `onValueChange`).', + 'Radix Slider in the carved-track skin. All Radix Slider props pass through (`defaultValue`, `min`, `max`, `step`, `onValueChange`). Set `marks` to carve step notches into the track — `marks` snaps to the Radix `step`.', }, }, }, + args: { defaultValue: [60], min: 0, max: 100, step: 1 }, + argTypes: { + defaultValue: { control: 'object', description: 'Uncontrolled starting value(s).' }, + min: { control: 'number' }, + max: { control: 'number' }, + step: { control: 'number', description: 'Snap increment (also drives auto `marks`).' }, + disabled: { control: 'boolean' }, + marks: { + control: 'boolean', + description: 'Step marks. `true` derives one per `step`; or pass an array for custom/labelled marks.', + }, + notches: { + control: 'inline-radio', + options: ['top', 'bottom', 'both', 'none'], + description: 'Notch tick placement relative to the track (labels still render when `none`).', + }, + knobStyle: { + control: 'inline-radio', + options: ['square', 'round'], + description: 'Knob shape: `square` (default) or `round`.', + }, + className: { control: 'text' }, + }, decorators: [(Story) =>
], } satisfies Meta; export default meta; type Story = StoryObj; -export const Playground: Story = { args: { defaultValue: [60], max: 100, step: 1 } }; +export const Playground: Story = {}; -export const Stepped: Story = { args: { defaultValue: [40], max: 100, step: 10 } }; +/** `marks` auto-derives one notch per `step`. */ +export const Stepped: Story = { + args: { defaultValue: [40], step: 10, marks: true }, +}; + +/** `notches='both'` carves ticks above and below the bar. */ +export const NotchesBoth: Story = { + args: { defaultValue: [60], step: 20, marks: true, notches: 'both' }, +}; + +/** Pass an array of `{ value, label }` for labelled ticks. */ +export const LabelledMarks: Story = { + args: { + defaultValue: [50], + step: 25, + notches: 'bottom', + marks: [ + { value: 0, label: 'Off' }, + { value: 25, label: 'Low' }, + { value: 50, label: 'Mid' }, + { value: 75, label: 'High' }, + { value: 100, label: 'Max' }, + ], + }, +}; + +export const Disabled: Story = { + args: { defaultValue: [30], step: 10, marks: true, disabled: true }, +}; diff --git a/src/stories/Tabs.stories.tsx b/src/stories/Tabs.stories.tsx index 9004e4c..c004fd7 100644 --- a/src/stories/Tabs.stories.tsx +++ b/src/stories/Tabs.stories.tsx @@ -12,6 +12,12 @@ const meta = { }, }, }, + argTypes: { + defaultValue: { control: 'text' }, + value: { control: 'text' }, + onValueChange: { action: 'value changed' }, + disabled: { control: 'boolean' }, + }, } satisfies Meta; export default meta; diff --git a/src/stories/TextField.stories.tsx b/src/stories/TextField.stories.tsx index aa6bdee..f81905e 100644 --- a/src/stories/TextField.stories.tsx +++ b/src/stories/TextField.stories.tsx @@ -13,6 +13,16 @@ const meta = { }, }, }, + argTypes: { + placeholder: { control: 'text' }, + value: { control: 'text' }, + defaultValue: { control: 'text' }, + disabled: { control: 'boolean' }, + readOnly: { control: 'boolean' }, + required: { control: 'boolean' }, + type: { control: 'text' }, + onChange: { action: 'changed' }, + }, args: { placeholder: 'Type here…' }, } satisfies Meta; diff --git a/src/stories/Window.stories.tsx b/src/stories/Window.stories.tsx index e36540a..337575b 100644 --- a/src/stories/Window.stories.tsx +++ b/src/stories/Window.stories.tsx @@ -12,6 +12,11 @@ const meta = { }, }, }, + argTypes: { + title: { control: 'text' }, + badge: { control: false }, + children: { control: false }, + }, args: { title: 'Finder' }, } satisfies Meta; diff --git a/src/styles/components.css b/src/styles/components.css index 14149fd..4ebdd2a 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -6,353 +6,1318 @@ ============================================================ */ @keyframes modern-sk-pop { - from { opacity: 0; transform: scale(.6); } - to { opacity: 1; transform: scale(1); } + from { + opacity: 0; + transform: scale(0.6); + } + to { + opacity: 1; + transform: scale(1); + } } @keyframes modern-sk-scale-in { - from { opacity: 0; transform: scale(.96); } - to { opacity: 1; transform: scale(1); } + from { + opacity: 0; + transform: scale(0.96); + } + to { + opacity: 1; + transform: scale(1); + } } @keyframes modern-sk-scale-out { - from { opacity: 1; transform: scale(1); } - to { opacity: 0; transform: scale(.96); } + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.96); + } +} +@keyframes modern-sk-fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@keyframes modern-sk-fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } } -@keyframes modern-sk-fade-in { from { opacity: 0; } to { opacity: 1; } } -@keyframes modern-sk-fade-out { from { opacity: 1; } to { opacity: 0; } } /* ---------- BUTTONS ---------- */ -.modern-sk-btn{ - font-family:var(--font-sans); font-size:var(--ctl-font); font-weight:600; - line-height:1.1; padding:var(--ctl-pad-y) var(--ctl-pad-x); - border-radius:var(--r-md); cursor:pointer; user-select:none; - display:inline-flex; align-items:center; justify-content:center; gap:7px; - border:1px solid var(--hair-strong); color:var(--fg-1); - background:var(--grad-key); - box-shadow:var(--shadow-raised); - transition:filter var(--dur-quick) var(--ease-out), - box-shadow var(--dur-quick) var(--ease-out), - transform var(--dur-press) var(--ease-out), - background var(--dur-quick) var(--ease-out); +.modern-sk-btn { + font-family: var(--font-sans); + font-size: var(--ctl-font); + font-weight: 600; + line-height: 1.1; + padding: var(--ctl-pad-y) var(--ctl-pad-x); + border-radius: var(--r-md); + cursor: pointer; + user-select: none; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 7px; + border: 1px solid var(--hair-strong); + color: var(--fg-1); + background: var(--grad-key); + box-shadow: var(--shadow-raised); + transition: + filter var(--dur-quick) var(--ease-out), + box-shadow var(--dur-quick) var(--ease-out), + transform var(--dur-press) var(--ease-out), + background var(--dur-quick) var(--ease-out); } -.modern-sk-btn:hover{ background:var(--grad-key-hover); } -.modern-sk-btn:active{ background:var(--grad-key-down); box-shadow:var(--shadow-pressed); transform:translateY(1px); } -.modern-sk-btn:focus-visible{ outline:none; box-shadow:var(--focus-ring), var(--shadow-raised); } -.modern-sk-btn[disabled], .modern-sk-btn.is-disabled{ opacity:.4; filter:saturate(.6); pointer-events:none; } -.modern-sk-btn .ph{ font-size:1.05em; display:inline-flex; } - -.modern-sk-btn--primary{ - color:var(--lime-ink); background:var(--grad-primary); - border-color:var(--lime-deep); - text-shadow:0 1px 0 rgba(255,255,255,.25); - box-shadow:0 1px 0 rgba(255,255,255,.3) inset, var(--glow-lime), 0 2px 4px rgba(0,0,0,.35); +.modern-sk-btn:hover { + background: var(--grad-key-hover); } -.modern-sk-btn--primary:hover{ filter:brightness(1.04); background:var(--grad-primary); } -.modern-sk-btn--primary:active{ background:var(--grad-primary-down); box-shadow:0 3px 6px rgba(60,90,10,.5) inset; transform:translateY(1px); } -.modern-sk-btn--primary:focus-visible{ box-shadow:var(--focus-ring), 0 1px 0 rgba(255,255,255,.3) inset, 0 2px 4px rgba(0,0,0,.35); } - -.modern-sk-btn--ember{ - color:#fff; background:var(--grad-ember); - border-color:var(--ember-deep); - text-shadow:0 1px 1px rgba(0,0,0,.25); - box-shadow:0 1px 0 rgba(255,255,255,.08) inset, 0 2px 4px rgba(0,0,0,.35), 0 6px 14px rgba(233,87,43,.18); +.modern-sk-btn:active { + background: var(--grad-key-down); + box-shadow: var(--shadow-pressed); + transform: translateY(1px); } -.modern-sk-btn--ember:hover{ background:var(--grad-ember); filter:brightness(1.06); } -.modern-sk-btn--ember:active{ background:var(--grad-ember-down); box-shadow:0 3px 6px rgba(90,30,10,.55) inset; transform:translateY(1px); } -.modern-sk-btn--ember:focus-visible{ box-shadow:var(--focus-ring-ember), 0 2px 4px rgba(0,0,0,.35); } - -.modern-sk-btn--ghost{ - color:var(--fg-2); background:transparent; border-color:var(--hair-strong); - box-shadow:none; text-shadow:none; +.modern-sk-btn:focus-visible { + outline: none; + box-shadow: var(--focus-ring), var(--shadow-raised); +} +.modern-sk-btn[disabled], +.modern-sk-btn.is-disabled { + opacity: 0.4; + filter: saturate(0.6); + pointer-events: none; +} +.modern-sk-btn .ph { + font-size: 1.05em; + display: inline-flex; } -.modern-sk-btn--ghost:hover{ background:rgba(255,255,255,.04); color:var(--fg-1); } -.modern-sk-btn--ghost:active{ background:rgba(0,0,0,.2); box-shadow:var(--shadow-pressed); transform:translateY(1px); } -.modern-sk-btn--sm{ font-size:12px; padding:4px 11px; gap:5px; } -.modern-sk-btn--icon{ padding:var(--ctl-pad-y); width:calc(var(--ctl-font) + 2*var(--ctl-pad-y)); aspect-ratio:1; } +.modern-sk-btn--primary { + color: var(--lime-ink); + background: var(--grad-primary); + border-color: var(--lime-deep); + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); + box-shadow: + 0 1px 0 rgba(255, 255, 255, 0.3) inset, + var(--glow-lime), + 0 2px 4px rgba(0, 0, 0, 0.35); +} +.modern-sk-btn--primary:hover { + filter: brightness(1.04); + background: var(--grad-primary); +} +.modern-sk-btn--primary:active { + background: var(--grad-primary-down); + box-shadow: 0 3px 6px rgba(60, 90, 10, 0.5) inset; + transform: translateY(1px); +} +.modern-sk-btn--primary:focus-visible { + box-shadow: + var(--focus-ring), + 0 1px 0 rgba(255, 255, 255, 0.3) inset, + 0 2px 4px rgba(0, 0, 0, 0.35); +} + +.modern-sk-btn--ember { + color: #fff; + background: var(--grad-ember); + border-color: var(--ember-deep); + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); + box-shadow: + 0 1px 0 rgba(255, 255, 255, 0.08) inset, + 0 2px 4px rgba(0, 0, 0, 0.35), + 0 6px 14px rgba(233, 87, 43, 0.18); +} +.modern-sk-btn--ember:hover { + background: var(--grad-ember); + filter: brightness(1.06); +} +.modern-sk-btn--ember:active { + background: var(--grad-ember-down); + box-shadow: 0 3px 6px rgba(90, 30, 10, 0.55) inset; + transform: translateY(1px); +} +.modern-sk-btn--ember:focus-visible { + box-shadow: + var(--focus-ring-ember), + 0 2px 4px rgba(0, 0, 0, 0.35); +} + +.modern-sk-btn--ghost { + color: var(--fg-2); + background: transparent; + border-color: var(--hair-strong); + box-shadow: none; + text-shadow: none; +} +.modern-sk-btn--ghost:hover { + background: rgba(255, 255, 255, 0.04); + color: var(--fg-1); +} +.modern-sk-btn--ghost:active { + background: rgba(0, 0, 0, 0.2); + box-shadow: var(--shadow-pressed); + transform: translateY(1px); +} + +.modern-sk-btn--sm { + font-size: 12px; + padding: 4px 11px; + gap: 5px; +} +.modern-sk-btn--icon { + padding: var(--ctl-pad-y); + width: calc(var(--ctl-font) + 2 * var(--ctl-pad-y)); + aspect-ratio: 1; +} /* ---------- TEXT FIELDS ---------- */ -.modern-sk-field{ - width:100%; font-family:var(--font-sans); font-size:14px; color:var(--fg-1); - padding:var(--field-pad-y) var(--field-pad-x); border-radius:var(--r-md); - background:var(--steel-900); border:1px solid var(--edge-inset); - box-shadow:var(--shadow-inset-well); outline:none; - transition:box-shadow var(--dur-quick) var(--ease-out), border-color var(--dur-quick) var(--ease-out); +.modern-sk-field { + width: 100%; + font-family: var(--font-sans); + font-size: 14px; + color: var(--fg-1); + padding: var(--field-pad-y) var(--field-pad-x); + border-radius: var(--r-md); + background: var(--steel-900); + border: 1px solid var(--edge-inset); + box-shadow: var(--shadow-inset-well); + outline: none; + transition: + box-shadow var(--dur-quick) var(--ease-out), + border-color var(--dur-quick) var(--ease-out); +} +.modern-sk-field::placeholder { + color: var(--fg-3); +} +.modern-sk-field:focus { + border-color: var(--lime-deep); + box-shadow: var(--shadow-inset-well), var(--focus-ring); +} +textarea.modern-sk-field { + resize: vertical; + min-height: 64px; + line-height: 1.5; } -.modern-sk-field::placeholder{ color:var(--fg-3); } -.modern-sk-field:focus{ border-color:var(--lime-deep); box-shadow:var(--shadow-inset-well), var(--focus-ring); } -textarea.modern-sk-field{ resize:vertical; min-height:64px; line-height:1.5; } -.modern-sk-search{ position:relative; display:flex; align-items:center; } -.modern-sk-search .ph{ position:absolute; left:11px; color:var(--fg-3); font-size:16px; pointer-events:none; display:inline-flex; } -.modern-sk-search .modern-sk-field{ padding-left:34px; } +.modern-sk-search { + position: relative; + display: flex; + align-items: center; +} +.modern-sk-search .ph { + position: absolute; + left: 11px; + color: var(--fg-3); + font-size: 16px; + pointer-events: none; + display: inline-flex; +} +.modern-sk-search .modern-sk-field { + padding-left: 34px; +} /* ---------- SELECT (Radix Select, styled as the glossy key) ---------- */ -.modern-sk-select{ - font-family:var(--font-sans); font-size:14px; color:var(--fg-1); cursor:pointer; - display:inline-flex; align-items:center; gap:10px; - padding:var(--field-pad-y) 12px var(--field-pad-y) var(--field-pad-x); - border-radius:var(--r-md); border:1px solid var(--hair-strong); - background:var(--grad-key); - box-shadow:var(--shadow-raised); outline:none; +.modern-sk-select { + font-family: var(--font-sans); + font-size: 14px; + color: var(--fg-1); + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 10px; + padding: var(--field-pad-y) 12px var(--field-pad-y) var(--field-pad-x); + border-radius: var(--r-md); + border: 1px solid var(--hair-strong); + background: var(--grad-key); + box-shadow: var(--shadow-raised); + outline: none; } -.modern-sk-select:hover{ background:var(--grad-key-hover); } -.modern-sk-select:focus-visible{ box-shadow:var(--focus-ring), var(--shadow-raised); } -.modern-sk-select__icon{ color:var(--fg-2); display:inline-flex; margin-left:auto; } -.modern-sk-select__content{ - min-width:var(--radix-select-trigger-width); padding:6px; border-radius:var(--r-lg); - border:1px solid var(--hair-strong); background:rgba(34,36,27,.86); - backdrop-filter:blur(24px) saturate(1.3); -webkit-backdrop-filter:blur(24px) saturate(1.3); - box-shadow:var(--shadow-window); z-index:50; - transform-origin:var(--radix-select-content-transform-origin); +.modern-sk-select:hover { + background: var(--grad-key-hover); } -.modern-sk-select__content[data-state="open"]{ animation:modern-sk-scale-in var(--dur-base) var(--ease-out); } -.modern-sk-select__item{ - display:flex; align-items:center; gap:10px; padding:7px 10px; border-radius:var(--r-sm); - font-size:13px; color:var(--fg-1); cursor:pointer; outline:none; user-select:none; +.modern-sk-select:focus-visible { + box-shadow: var(--focus-ring), var(--shadow-raised); +} +.modern-sk-select__icon { + color: var(--fg-2); + display: inline-flex; + margin-left: auto; +} +.modern-sk-select__content { + min-width: var(--radix-select-trigger-width); + padding: 6px; + border-radius: var(--r-lg); + border: 1px solid var(--hair-strong); + background: rgba(34, 36, 27, 0.86); + backdrop-filter: blur(24px) saturate(1.3); + -webkit-backdrop-filter: blur(24px) saturate(1.3); + box-shadow: var(--shadow-window); + z-index: 50; + transform-origin: var(--radix-select-content-transform-origin); +} +/* slide out of the trigger: grows from the radix transform-origin while + sliding in from the open side. Symmetric, token-driven exit. */ +.modern-sk-select__content[data-side='bottom'] { + --sel-from-y: -6px; +} +.modern-sk-select__content[data-side='top'] { + --sel-from-y: 6px; +} +.modern-sk-select__content[data-side='left'], +.modern-sk-select__content[data-side='right'] { + --sel-from-y: 0px; +} +.modern-sk-select__content[data-state='open'] { + animation: modern-sk-select-in var(--dur-base) var(--ease-snap); +} +.modern-sk-select__content[data-state='closed'] { + animation: modern-sk-select-out var(--dur-quick) var(--ease-out); +} +@keyframes modern-sk-select-in { + from { + opacity: 0; + transform: translateY(var(--sel-from-y, -6px)) scale(0.94); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} +@keyframes modern-sk-select-out { + from { + opacity: 1; + transform: translateY(0) scale(1); + } + to { + opacity: 0; + transform: translateY(var(--sel-from-y, -6px)) scale(0.96); + } +} +@media (prefers-reduced-motion: reduce) { + .modern-sk-select__content[data-state='open'], + .modern-sk-select__content[data-state='closed'] { + animation: modern-sk-fade-in var(--dur-quick) var(--ease-out); + } +} +.modern-sk-select__item { + display: flex; + align-items: center; + gap: 10px; + padding: 7px 10px; + border-radius: var(--r-sm); + font-size: 13px; + color: var(--fg-1); + cursor: pointer; + outline: none; + user-select: none; +} +.modern-sk-select__item[data-highlighted] { + background: linear-gradient(180deg, var(--lime), var(--lime-deep)); + color: var(--lime-ink); +} +.modern-sk-select__item-indicator { + margin-left: auto; + display: inline-flex; } -.modern-sk-select__item[data-highlighted]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); color:var(--lime-ink); } -.modern-sk-select__item-indicator{ margin-left:auto; display:inline-flex; } /* ---------- SWITCH ---------- */ -.modern-sk-switch{ - position:relative; width:var(--switch-w); height:var(--switch-h); - flex-shrink:0; border-radius:8px; padding:0; border:1px solid var(--edge-inset); - background:var(--steel-700); box-shadow:var(--shadow-inset-well); cursor:pointer; - transition:background var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); +.modern-sk-switch { + position: relative; + width: var(--switch-w); + height: var(--switch-h); + flex-shrink: 0; + border-radius: 8px; + padding: 0; + border: 1px solid var(--edge-inset); + background: var(--steel-700); + box-shadow: var(--shadow-inset-well); + cursor: pointer; + transition: + background var(--dur-base) var(--ease-out), + border-color var(--dur-base) var(--ease-out); } -.modern-sk-switch__thumb{ - display:block; position:absolute; top:50%; left:var(--switch-gap); transform:translateY(-50%); - width:var(--switch-knob); height:var(--switch-knob); border-radius:5px; - background:linear-gradient(180deg,#cfd1c4,#a7a99c); - box-shadow:0 1px 2px rgba(0,0,0,.4), 0 1px 0 rgba(255,255,255,.7) inset; - transition:left var(--dur-base) var(--ease-snap), background var(--dur-base) var(--ease-out); +.modern-sk-switch__thumb { + display: block; + position: absolute; + top: 50%; + left: var(--switch-gap); + transform: translateY(-50%); + width: var(--switch-knob); + height: var(--switch-knob); + border-radius: 5px; + background: linear-gradient(180deg, #cfd1c4, #a7a99c); + box-shadow: + 0 1px 2px rgba(0, 0, 0, 0.4), + 0 1px 0 rgba(255, 255, 255, 0.7) inset; + transition: + left var(--dur-base) var(--ease-snap), + background var(--dur-base) var(--ease-out); +} +.modern-sk-switch[data-state='checked'] { + background: linear-gradient(180deg, var(--lime), var(--lime-deep)); + border-color: var(--lime-deep); +} +.modern-sk-switch[data-state='checked'] .modern-sk-switch__thumb { + left: calc(100% - var(--switch-knob) - var(--switch-gap)); + background: linear-gradient(180deg, #fff, #e6e8dd); +} +.modern-sk-switch:focus-visible { + outline: none; + box-shadow: var(--shadow-inset-well), var(--focus-ring); } -.modern-sk-switch[data-state="checked"]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); border-color:var(--lime-deep); } -.modern-sk-switch[data-state="checked"] .modern-sk-switch__thumb{ left:calc(100% - var(--switch-knob) - var(--switch-gap)); background:linear-gradient(180deg,#fff,#e6e8dd); } -.modern-sk-switch:focus-visible{ outline:none; box-shadow:var(--shadow-inset-well), var(--focus-ring); } /* ---------- CHECKBOX ---------- */ -.modern-sk-check{ - width:22px; height:22px; flex-shrink:0; border-radius:4px; padding:0; - background:var(--steel-900); border:1px solid var(--edge-inset); - box-shadow:var(--shadow-inset-well); cursor:pointer; - display:flex; align-items:center; justify-content:center; - transition:background var(--dur-quick) var(--ease-out), border-color var(--dur-quick) var(--ease-out); +.modern-sk-check { + width: 22px; + height: 22px; + flex-shrink: 0; + border-radius: 4px; + padding: 0; + background: var(--steel-900); + border: 1px solid var(--edge-inset); + box-shadow: var(--shadow-inset-well); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: + background var(--dur-quick) var(--ease-out), + border-color var(--dur-quick) var(--ease-out); +} +.modern-sk-check[data-state='checked'] { + background: linear-gradient(180deg, var(--lime-bright), var(--lime-deep)); + border-color: var(--lime-deep); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4) inset; +} +.modern-sk-check__indicator { + display: flex; + align-items: center; + justify-content: center; + animation: modern-sk-pop var(--dur-quick) var(--ease-snap); +} +.modern-sk-check__indicator svg { + width: 13px; + height: 13px; + display: block; +} +.modern-sk-check:focus-visible { + outline: none; + box-shadow: var(--shadow-inset-well), var(--focus-ring); } -.modern-sk-check[data-state="checked"]{ background:linear-gradient(180deg,var(--lime-bright),var(--lime-deep)); border-color:var(--lime-deep); box-shadow:0 1px 0 rgba(255,255,255,.4) inset; } -.modern-sk-check__indicator{ display:flex; align-items:center; justify-content:center; animation:modern-sk-pop var(--dur-quick) var(--ease-snap); } -.modern-sk-check__indicator svg{ width:13px; height:13px; display:block; } -.modern-sk-check:focus-visible{ outline:none; box-shadow:var(--shadow-inset-well), var(--focus-ring); } /* ---------- RADIO ---------- */ -.modern-sk-radio{ - width:22px; height:22px; flex-shrink:0; border-radius:50%; padding:0; - background:var(--steel-900); border:1px solid var(--edge-inset); - box-shadow:var(--shadow-inset-well); cursor:pointer; - display:flex; align-items:center; justify-content:center; - transition:border-color var(--dur-quick) var(--ease-out); +.modern-sk-radio { + width: 22px; + height: 22px; + flex-shrink: 0; + border-radius: 50%; + padding: 0; + background: var(--steel-900); + border: 1px solid var(--edge-inset); + box-shadow: var(--shadow-inset-well); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: border-color var(--dur-quick) var(--ease-out); } -.modern-sk-radio__indicator{ display:flex; align-items:center; justify-content:center; width:100%; height:100%; } -.modern-sk-radio__indicator::after{ - content:""; width:11px; height:11px; border-radius:50%; - background:radial-gradient(circle at 50% 38%,var(--lime-bright),var(--lime-deep)); - box-shadow:0 0 8px rgba(190,242,100,.45), 0 1px 0 rgba(255,255,255,.45) inset; - animation:modern-sk-pop var(--dur-quick) var(--ease-snap); +.modern-sk-radio__indicator { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} +.modern-sk-radio__indicator::after { + content: ''; + width: 11px; + height: 11px; + border-radius: 50%; + background: radial-gradient( + circle at 50% 38%, + var(--lime-bright), + var(--lime-deep) + ); + box-shadow: + 0 0 8px rgba(190, 242, 100, 0.45), + 0 1px 0 rgba(255, 255, 255, 0.45) inset; + animation: modern-sk-pop var(--dur-quick) var(--ease-snap); +} +.modern-sk-radio:focus-visible { + outline: none; + box-shadow: var(--shadow-inset-well), var(--focus-ring); } -.modern-sk-radio:focus-visible{ outline:none; box-shadow:var(--shadow-inset-well), var(--focus-ring); } /* control + label row helper */ -.modern-sk-control{ display:inline-flex; align-items:center; gap:10px; font-size:14px; color:var(--fg-2); cursor:pointer; } +.modern-sk-control { + display: inline-flex; + align-items: center; + gap: 10px; + font-size: 14px; + color: var(--fg-2); + cursor: pointer; +} /* ---------- SEGMENTED CONTROL (Radix ToggleGroup) ---------- */ -.modern-sk-seg{ position:relative; display:inline-flex; background:var(--steel-900); border:1px solid var(--edge-inset); border-radius:var(--r-md); padding:3px; box-shadow:var(--shadow-inset-well); gap:2px; } -.modern-sk-seg__thumb{ position:absolute; top:3px; left:0; height:calc(100% - 6px); background:var(--grad-key); border-radius:var(--r-sm); box-shadow:var(--shadow-raised); pointer-events:none; transition:transform 180ms var(--ease-snap), width 180ms var(--ease-snap); will-change:transform,width; } -.modern-sk-seg__item{ position:relative; z-index:1; font-family:var(--font-sans); font-size:13px; font-weight:600; color:var(--fg-2); background:transparent; border:none; padding:var(--seg-pad-y) 14px; border-radius:var(--r-sm); cursor:pointer; transition:color var(--dur-quick); } -.modern-sk-seg__item:hover{ color:var(--fg-1); } -.modern-sk-seg__item[data-state="on"]{ color:var(--fg-1); } +.modern-sk-seg { + position: relative; + display: inline-flex; + background: var(--steel-900); + border: 1px solid var(--edge-inset); + border-radius: var(--r-md); + padding: 3px; + box-shadow: var(--shadow-inset-well); + gap: 2px; +} +.modern-sk-seg__thumb { + position: absolute; + top: 3px; + left: 0; + height: calc(100% - 6px); + background: var(--grad-key); + border-radius: var(--r-sm); + box-shadow: var(--shadow-raised); + pointer-events: none; + transition: + transform 180ms var(--ease-snap), + width 180ms var(--ease-snap); + will-change: transform, width; +} +.modern-sk-seg__item { + position: relative; + z-index: 1; + font-family: var(--font-sans); + font-size: 13px; + font-weight: 600; + color: var(--fg-2); + background: transparent; + border: none; + padding: var(--seg-pad-y) 14px; + border-radius: var(--r-sm); + cursor: pointer; + transition: color var(--dur-quick); +} +.modern-sk-seg__item:hover { + color: var(--fg-1); +} +.modern-sk-seg__item[data-state='on'] { + color: var(--fg-1); +} /* ---------- SLIDER ---------- */ -.modern-sk-slider{ position:relative; display:flex; align-items:center; width:200px; height:20px; user-select:none; touch-action:none; } -.modern-sk-slider__track{ position:relative; flex-grow:1; height:6px; border-radius:3px; background:var(--steel-800); box-shadow:var(--shadow-inset-well); } -.modern-sk-slider__range{ position:absolute; height:100%; border-radius:3px; background:linear-gradient(90deg,var(--lime-deep),var(--lime)); box-shadow:0 0 10px rgba(190,242,100,.4); } -.modern-sk-slider__thumb{ display:block; width:20px; height:20px; border-radius:50%; background:linear-gradient(180deg,#fff,#e6e8dd); box-shadow:0 2px 5px rgba(0,0,0,.5), 0 1px 0 rgba(255,255,255,.9) inset; cursor:pointer; outline:none; } -.modern-sk-slider__thumb:focus-visible{ box-shadow:0 2px 5px rgba(0,0,0,.5), 0 1px 0 rgba(255,255,255,.9) inset, var(--focus-ring); } -.modern-sk-slider--has-steps{ padding-bottom:22px; } -.modern-sk-slider__step-dot{ position:absolute; top:50%; width:4px; height:4px; border-radius:50%; background:var(--steel-500); transform:translate(-50%,-50%); z-index:1; pointer-events:none; } -.modern-sk-slider__steps{ position:absolute; left:10px; right:10px; top:calc(50% + 6px); pointer-events:none; } -.modern-sk-slider__step{ position:absolute; transform:translateX(-50%); display:flex; flex-direction:column; align-items:center; gap:3px; } -.modern-sk-slider__step-tick{ width:1px; height:5px; background:var(--steel-600); } -.modern-sk-slider__step-label{ font-family:var(--font-mono); font-size:10px; line-height:1; color:var(--fg-3); white-space:nowrap; } +.modern-sk-slider { + --ms-thumb: 20px; + --ms-thumb-w: var(--ms-thumb); + position: relative; + display: flex; + align-items: center; + width: 200px; + height: var(--ms-thumb); + user-select: none; + touch-action: none; +} +.modern-sk-slider__track { + position: relative; + flex-grow: 1; + height: 6px; + border-radius: 3px; + background: var(--steel-800); + box-shadow: var(--shadow-inset-well); +} +.modern-sk-slider__range { + position: absolute; + height: 100%; + border-radius: 3px; + background: linear-gradient(90deg, var(--lime-deep), var(--lime)); + box-shadow: 0 0 10px rgba(190, 242, 100, 0.4); +} +.modern-sk-slider__thumb { + display: block; + width: var(--ms-thumb-w); + height: var(--ms-thumb); + border-radius: 4px; + background: linear-gradient(180deg, #fff, #e6e8dd); + box-shadow: + 0 2px 5px rgba(0, 0, 0, 0.5), + 0 1px 0 rgba(255, 255, 255, 0.9) inset; + cursor: pointer; + outline: none; +} +.modern-sk-slider--knob-square .modern-sk-slider__thumb { + --ms-thumb-w: 14px; +} +.modern-sk-slider--knob-round .modern-sk-slider__thumb { + --ms-thumb-w: var(--ms-thumb); + border-radius: 50%; +} +.modern-sk-slider__thumb:focus-visible { + box-shadow: + 0 2px 5px rgba(0, 0, 0, 0.5), + 0 1px 0 rgba(255, 255, 255, 0.9) inset, + var(--focus-ring); +} + +/* Notches sit OUTSIDE the bar, anchored to the track edges. Horizontal position tracks + the thumb centre: it travels from thumb-radius to (width - radius). Reserve layout + room with padding so ticks/labels never collide with neighbours. */ +.modern-sk-slider--notch-top { + padding-top: 14px; +} +.modern-sk-slider--notch-bottom { + padding-bottom: 14px; +} +.modern-sk-slider--has-labels { + padding-bottom: 18px; +} +.modern-sk-slider--notch-bottom.modern-sk-slider--has-labels { + padding-bottom: 30px; +} + +.modern-sk-slider__notches { + position: absolute; + left: 0; + right: 0; + height: 0; + pointer-events: none; +} +.modern-sk-slider__notches--top { + bottom: calc(100% + 7px); +} +.modern-sk-slider__notches--bottom { + top: calc(100% + 7px); +} +.modern-sk-slider__notch { + position: absolute; + left: calc(var(--ms-thumb) / 2 + (100% - var(--ms-thumb)) * var(--p)); + width: 3px; + height: 3px; + border-radius: 99px; + background: var(--steel-500); + transform: translateX(-50%); +} +.modern-sk-slider__notches--top .modern-sk-slider__notch { + bottom: 0; +} +.modern-sk-slider__notches--bottom .modern-sk-slider__notch { + top: 0; +} + +.modern-sk-slider__labels { + position: absolute; + left: 0; + right: 0; + top: calc(100% + 8px); + pointer-events: none; +} +.modern-sk-slider--notch-bottom .modern-sk-slider__labels { + top: calc(100% + 18px); +} +.modern-sk-slider__label { + position: absolute; + left: calc(var(--ms-thumb) / 2 + (100% - var(--ms-thumb)) * var(--p)); + transform: translateX(-50%); + font-family: var(--font-mono); + font-size: 10px; + line-height: 1; + color: var(--fg-3); + white-space: nowrap; +} /* ---------- STEPPER ---------- */ -.modern-sk-stepper{ display:inline-flex; border-radius:var(--r-md); overflow:hidden; box-shadow:var(--shadow-raised); border:1px solid var(--hair-strong); } -.modern-sk-stepper button{ font-family:var(--font-mono); font-size:16px; font-weight:600; color:var(--fg-1); background:var(--grad-key); border:none; width:34px; height:var(--stepper-h); cursor:pointer; transition:background var(--dur-quick); } -.modern-sk-stepper button:hover{ background:var(--grad-key-hover); } -.modern-sk-stepper button:active{ background:var(--grad-key-down); } -.modern-sk-stepper button:first-child{ border-right:1px solid var(--edge-inset); } +.modern-sk-stepper { + display: inline-flex; + border-radius: var(--r-md); + overflow: hidden; + box-shadow: var(--shadow-raised); + border: 1px solid var(--hair-strong); +} +.modern-sk-stepper button { + font-family: var(--font-mono); + font-size: 16px; + font-weight: 600; + color: var(--fg-1); + background: var(--grad-key); + border: none; + width: 34px; + height: var(--stepper-h); + cursor: pointer; + transition: background var(--dur-quick); +} +.modern-sk-stepper button:hover { + background: var(--grad-key-hover); +} +.modern-sk-stepper button:active { + background: var(--grad-key-down); +} +.modern-sk-stepper button:first-child { + border-right: 1px solid var(--edge-inset); +} /* ---------- BADGES / CHIPS / TAGS ---------- */ -.modern-sk-badge{ display:inline-flex; align-items:center; gap:5px; font-size:11px; font-weight:700; line-height:1; padding:4px 9px; border-radius:var(--r-pill); letter-spacing:.02em; border:1px solid transparent; } -.modern-sk-badge--lime{ color:var(--lime-ink); background:linear-gradient(180deg,var(--lime-bright),var(--lime-deep)); border-color:var(--lime-deep); } -.modern-sk-badge--ember{ color:#fff; background:var(--grad-ember); border-color:var(--ember-deep); } -.modern-sk-badge--neutral{ color:var(--fg-2); background:var(--steel-700); border-color:var(--hair-strong); } -.modern-sk-badge--outline{ color:var(--fg-2); background:transparent; border-color:var(--hair-strong); } -.modern-sk-badge--dot::before{ content:""; width:6px; height:6px; border-radius:50%; background:currentColor; } +.modern-sk-badge { + display: inline-flex; + align-items: center; + gap: 5px; + font-size: 11px; + font-weight: 700; + line-height: 1; + padding: 4px 9px; + border-radius: var(--r-pill); + letter-spacing: 0.02em; + border: 1px solid transparent; +} +.modern-sk-badge--lime { + color: var(--lime-ink); + background: linear-gradient(180deg, var(--lime-bright), var(--lime-deep)); + border-color: var(--lime-deep); +} +.modern-sk-badge--ember { + color: #fff; + background: var(--grad-ember); + border-color: var(--ember-deep); +} +.modern-sk-badge--neutral { + color: var(--fg-2); + background: var(--steel-700); + border-color: var(--hair-strong); +} +.modern-sk-badge--outline { + color: var(--fg-2); + background: transparent; + border-color: var(--hair-strong); +} +.modern-sk-badge--dot::before { + content: ''; + width: 6px; + height: 6px; + border-radius: 50%; + background: currentColor; +} -.modern-sk-chip{ display:inline-flex; align-items:center; gap:6px; font-size:13px; font-weight:500; color:var(--fg-1); padding:5px 11px; border-radius:var(--r-pill); background:var(--steel-700); border:1px solid var(--hair-strong); } -.modern-sk-chip .x{ color:var(--fg-3); cursor:pointer; font-size:14px; line-height:1; background:none; border:none; padding:0; display:inline-flex; } -.modern-sk-chip .x:hover{ color:var(--fg-1); } +.modern-sk-chip { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 13px; + font-weight: 500; + color: var(--fg-1); + padding: 5px 11px; + border-radius: var(--r-pill); + background: var(--steel-700); + border: 1px solid var(--hair-strong); +} +.modern-sk-chip .x { + color: var(--fg-3); + cursor: pointer; + font-size: 14px; + line-height: 1; + background: none; + border: none; + padding: 0; + display: inline-flex; +} +.modern-sk-chip .x:hover { + color: var(--fg-1); +} /* ---------- CARD ---------- */ -.modern-sk-card{ background:var(--steel-900); border:1px solid var(--hair); border-radius:var(--r-lg); box-shadow:var(--shadow-card); padding:18px; } +.modern-sk-card { + background: var(--steel-900); + border: 1px solid var(--hair); + border-radius: var(--r-lg); + box-shadow: var(--shadow-card); + padding: 18px; +} /* ---------- LIST ROWS ---------- */ -.modern-sk-list{ background:var(--steel-900); border:1px solid var(--hair); border-radius:var(--r-lg); box-shadow:var(--shadow-card); overflow:hidden; } -.modern-sk-row{ display:flex; align-items:center; gap:12px; padding:10px 14px; border-bottom:1px solid var(--hair); cursor:default; } -.modern-sk-row:last-child{ border-bottom:none; } -.modern-sk-row:hover{ background:rgba(255,255,255,.03); } -.modern-sk-row.is-selected{ background:linear-gradient(180deg,rgba(190,242,100,.16),rgba(190,242,100,.08)); } -.modern-sk-row .nm{ font-size:14px; font-weight:500; color:var(--fg-1); white-space:nowrap; } -.modern-sk-row .meta{ margin-left:auto; font-family:var(--font-mono); font-size:11px; color:var(--fg-3); } +.modern-sk-list { + background: var(--steel-900); + border: 1px solid var(--hair); + border-radius: var(--r-lg); + box-shadow: var(--shadow-card); + overflow: hidden; +} +.modern-sk-row { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 14px; + border-bottom: 1px solid var(--hair); + cursor: default; + font-size: 14px; + color: var(--fg-1); +} +.modern-sk-row:last-child { + border-bottom: none; +} +.modern-sk-row:hover { + background: var(--steel-800); +} +.modern-sk-row.is-selected { + background: linear-gradient( + 180deg, + rgba(190, 242, 100, 0.16), + rgba(190, 242, 100, 0.08) + ); +} +.modern-sk-row .nm { + font-size: 14px; + font-weight: 500; + color: var(--fg-1); + white-space: nowrap; +} +.modern-sk-row .meta { + margin-left: auto; + font-family: var(--font-mono); + font-size: 11px; + color: var(--fg-3); +} /* ---------- MENU / POPOVER (Radix DropdownMenu) ---------- */ -.modern-sk-menu{ min-width:208px; padding:6px; border-radius:var(--r-lg); border:1px solid var(--hair-strong); background:rgba(34,36,27,.86); backdrop-filter:blur(24px) saturate(1.3); -webkit-backdrop-filter:blur(24px) saturate(1.3); box-shadow:var(--shadow-window); z-index:50; transform-origin:var(--radix-dropdown-menu-content-transform-origin); } -.modern-sk-menu[data-state="open"]{ animation:modern-sk-scale-in var(--dur-base) var(--ease-out); } -.modern-sk-menu[data-state="closed"]{ animation:modern-sk-scale-out var(--dur-quick) var(--ease-out); } -.modern-sk-menu-item{ display:flex; align-items:center; gap:10px; padding:7px 10px; border-radius:var(--r-sm); font-size:13px; color:var(--fg-1); cursor:pointer; outline:none; user-select:none; } -.modern-sk-menu-item .ph{ font-size:16px; color:var(--fg-2); display:inline-flex; } -.modern-sk-menu-item .sc{ margin-left:auto; font-family:var(--font-mono); font-size:11px; color:var(--fg-3); } -.modern-sk-menu-item[data-highlighted]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); color:var(--lime-ink); } -.modern-sk-menu-item[data-highlighted] .ph, .modern-sk-menu-item[data-highlighted] .sc{ color:var(--lime-ink); } -.modern-sk-menu-sep{ height:1px; background:var(--hair); margin:5px 4px; } +.modern-sk-menu { + min-width: 208px; + padding: 6px; + border-radius: var(--r-lg); + border: 1px solid var(--hair-strong); + background: rgba(34, 36, 27, 0.86); + backdrop-filter: blur(24px) saturate(1.3); + -webkit-backdrop-filter: blur(24px) saturate(1.3); + box-shadow: var(--shadow-window); + z-index: 50; + transform-origin: var(--radix-dropdown-menu-content-transform-origin); +} +.modern-sk-menu[data-state='open'] { + animation: modern-sk-scale-in var(--dur-base) var(--ease-out); +} +.modern-sk-menu[data-state='closed'] { + animation: modern-sk-scale-out var(--dur-quick) var(--ease-out); +} +.modern-sk-menu-item { + display: flex; + align-items: center; + gap: 10px; + padding: 7px 10px; + border-radius: var(--r-sm); + font-size: 13px; + color: var(--fg-1); + cursor: pointer; + outline: none; + user-select: none; +} +.modern-sk-menu-item .ph { + font-size: 16px; + color: var(--fg-2); + display: inline-flex; +} +.modern-sk-menu-item .sc { + margin-left: auto; + font-family: var(--font-mono); + font-size: 11px; + color: var(--fg-3); +} +.modern-sk-menu-item[data-highlighted] { + background: linear-gradient(180deg, var(--lime), var(--lime-deep)); + color: var(--lime-ink); +} +.modern-sk-menu-item[data-highlighted] .ph, +.modern-sk-menu-item[data-highlighted] .sc { + color: var(--lime-ink); +} +.modern-sk-menu-sep { + height: 1px; + background: var(--hair); + margin: 5px 4px; +} /* ---------- TABS (Radix Tabs) ---------- */ -.modern-sk-tabs{ display:flex; gap:2px; border-bottom:1px solid var(--hair); } -.modern-sk-tabs__trigger{ font-family:var(--font-sans); font-size:13px; font-weight:600; color:var(--fg-3); background:transparent; border:none; padding:9px 14px; cursor:pointer; position:relative; transition:color var(--dur-quick); } -.modern-sk-tabs__trigger:hover{ color:var(--fg-2); } -.modern-sk-tabs__trigger[data-state="active"]{ color:var(--fg-1); } -.modern-sk-tabs__trigger[data-state="active"]::after{ content:""; position:absolute; left:8px; right:8px; bottom:-1px; height:2px; border-radius:2px; background:var(--lime); box-shadow:0 0 8px rgba(190,242,100,.5); } +.modern-sk-tabs { + display: flex; + gap: 2px; + border-bottom: 1px solid var(--hair); +} +.modern-sk-tabs__trigger { + font-family: var(--font-sans); + font-size: 13px; + font-weight: 600; + color: var(--fg-3); + background: transparent; + border: none; + padding: 9px 14px; + cursor: pointer; + position: relative; + transition: color var(--dur-quick); +} +.modern-sk-tabs__trigger:hover { + color: var(--fg-2); +} +.modern-sk-tabs__trigger[data-state='active'] { + color: var(--fg-1); +} +.modern-sk-tabs__trigger[data-state='active']::after { + content: ''; + position: absolute; + left: 8px; + right: 8px; + bottom: -1px; + height: 2px; + border-radius: 2px; + background: var(--lime); + box-shadow: 0 0 8px rgba(190, 242, 100, 0.5); +} /* ---------- PROGRESS (Radix Progress) ---------- */ -.modern-sk-progress{ width:100%; height:8px; border-radius:var(--r-pill); background:var(--steel-800); box-shadow:var(--shadow-inset-well); overflow:hidden; } -.modern-sk-progress__indicator{ display:block; height:100%; border-radius:var(--r-pill); background:linear-gradient(90deg,var(--lime-deep),var(--lime)); box-shadow:0 0 10px rgba(190,242,100,.4); transition:width var(--dur-base) var(--ease-out); } +.modern-sk-progress { + width: 100%; + height: 8px; + border-radius: var(--r-pill); + background: var(--steel-800); + box-shadow: var(--shadow-inset-well); + overflow: hidden; +} +.modern-sk-progress__indicator { + display: block; + height: 100%; + border-radius: var(--r-pill); + background: linear-gradient(90deg, var(--lime-deep), var(--lime)); + box-shadow: 0 0 10px rgba(190, 242, 100, 0.4); + transition: width var(--dur-base) var(--ease-out); +} /* ---------- WINDOW CHROME ---------- */ -.modern-sk-window{ border-radius:var(--r-xl); overflow:hidden; border:1px solid var(--hair-strong); background:var(--steel-800); box-shadow:var(--shadow-window); } -.modern-sk-titlebar{ height:42px; display:flex; align-items:center; gap:9px; padding:0 14px; background:var(--grad-titlebar); border-bottom:1px solid rgba(0,0,0,.4); box-shadow:0 1px 0 rgba(255,255,255,.05) inset; } -.modern-sk-titlebar .ttl{ margin-left:6px; font-size:13px; font-weight:600; color:var(--fg-2); } -.modern-sk-traffic{ width:12px; height:12px; border-radius:50%; box-shadow:0 1px 1px rgba(0,0,0,.4) inset, 0 0 0 .5px rgba(0,0,0,.3); } -.modern-sk-traffic.r{ background:radial-gradient(circle at 35% 30%,#ff8a7a,#ec5f55); } -.modern-sk-traffic.y{ background:radial-gradient(circle at 35% 30%,#ffd97a,#e6a93c); } -.modern-sk-traffic.g{ background:radial-gradient(circle at 35% 30%,#bff07a,#9bce4c); } +.modern-sk-window { + border-radius: var(--r-xl); + overflow: hidden; + border: 1px solid var(--hair-strong); + background: var(--steel-800); + box-shadow: var(--shadow-window); +} +.modern-sk-titlebar { + height: 42px; + display: flex; + align-items: center; + gap: 9px; + padding: 0 14px; + background: var(--grad-titlebar); + border-bottom: 1px solid rgba(0, 0, 0, 0.4); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset; +} +.modern-sk-titlebar .ttl { + margin-left: 6px; + font-size: 13px; + font-weight: 600; + color: var(--fg-2); +} +.modern-sk-traffic { + width: 12px; + height: 12px; + border-radius: 50%; + box-shadow: + 0 1px 1px rgba(0, 0, 0, 0.4) inset, + 0 0 0 0.5px rgba(0, 0, 0, 0.3); +} +.modern-sk-traffic.r { + background: radial-gradient(circle at 35% 30%, #ff8a7a, #ec5f55); +} +.modern-sk-traffic.y { + background: radial-gradient(circle at 35% 30%, #ffd97a, #e6a93c); +} +.modern-sk-traffic.g { + background: radial-gradient(circle at 35% 30%, #bff07a, #9bce4c); +} /* ---------- TOOLTIP (Radix Tooltip) ---------- */ -.modern-sk-tooltip{ display:inline-flex; align-items:center; font-size:12px; font-weight:500; color:var(--fg-1); padding:5px 9px; border-radius:var(--r-sm); background:rgba(20,21,15,.92); border:1px solid var(--hair-strong); box-shadow:var(--shadow-card); z-index:60; transform-origin:var(--radix-tooltip-content-transform-origin); } -.modern-sk-tooltip[data-state="delayed-open"], .modern-sk-tooltip[data-state="instant-open"]{ animation:modern-sk-scale-in var(--dur-base) var(--ease-out); } -.modern-sk-tooltip[data-state="closed"]{ animation:modern-sk-fade-out var(--dur-quick) var(--ease-out); } +.modern-sk-tooltip { + display: inline-flex; + align-items: center; + font-size: 12px; + font-weight: 500; + color: var(--fg-1); + padding: 5px 9px; + border-radius: var(--r-sm); + background: rgba(20, 21, 15, 0.92); + border: 1px solid var(--hair-strong); + box-shadow: var(--shadow-card); + z-index: 60; + transform-origin: var(--radix-tooltip-content-transform-origin); +} +.modern-sk-tooltip[data-state='delayed-open'], +.modern-sk-tooltip[data-state='instant-open'] { + animation: modern-sk-scale-in var(--dur-base) var(--ease-out); +} +.modern-sk-tooltip[data-state='closed'] { + animation: modern-sk-fade-out var(--dur-quick) var(--ease-out); +} /* ---------- ICON BUTTON ---------- Square tactile key. Reuses the .modern-sk-btn engine; variants below just recolor. Pair with .modern-sk-btn / .modern-sk-btn--primary etc. */ -.modern-sk-iconbtn{ padding:0; width:32px; height:32px; aspect-ratio:1; display:inline-flex; align-items:center; justify-content:center; } -.modern-sk-iconbtn--sm{ width:26px; height:26px; } -.modern-sk-iconbtn--lg{ width:38px; height:38px; } +.modern-sk-iconbtn { + padding: 0; + width: 32px; + height: 32px; + aspect-ratio: 1; + display: inline-flex; + align-items: center; + justify-content: center; +} +.modern-sk-iconbtn--sm { + width: 26px; + height: 26px; +} +.modern-sk-iconbtn--lg { + width: 38px; + height: 38px; +} /* ---------- SPINNER ---------- - A carved donut groove (sunk like the switch well) with a glossy lime - arc spinning *inside* the channel. All drawn in SVG; CSS only sizes - the box and drives the rotation. Honors reduced motion. */ -.modern-sk-spinner{ display:inline-block; width:24px; height:24px; line-height:0; } -.modern-sk-spinner--sm{ width:16px; height:16px; } -.modern-sk-spinner--lg{ width:34px; height:34px; } -.modern-sk-spinner svg{ width:100%; height:100%; display:block; } -.modern-sk-spinner__arc{ - transform-origin:center; animation:modern-sk-spin .7s linear infinite; - filter:drop-shadow(0 0 3px rgba(190,242,100,.55)); + A carved donut groove (flat felt sunk by an SVG inner shadow, like the + switch well) with a glowing lime arc spinning in the channel. Groove + recess + arc glow are SVG filters; CSS only sizes the box and drives + the rotation. Honors reduced motion. */ +.modern-sk-spinner { + display: inline-block; + width: 24px; + height: 24px; + line-height: 0; +} +.modern-sk-spinner--sm { + width: 16px; + height: 16px; +} +.modern-sk-spinner--lg { + width: 34px; + height: 34px; +} +.modern-sk-spinner svg { + width: 100%; + height: 100%; + display: block; + overflow: visible; /* let the arc glow spill past the box, not clip square */ +} +.modern-sk-spinner__arc { + transform-origin: center; + animation: modern-sk-spin 0.7s linear infinite; +} +@keyframes modern-sk-spin { + to { + transform: rotate(360deg); + } +} +@media (prefers-reduced-motion: reduce) { + .modern-sk-spinner__arc { + animation-duration: 1.8s; + } } -@keyframes modern-sk-spin{ to{ transform:rotate(360deg); } } -@media (prefers-reduced-motion:reduce){ .modern-sk-spinner__arc{ animation-duration:1.8s; } } /* ---------- CALLOUT ---------- Soft semantic surface (12–14% tint on the felt) with a glossy icon chip. Variants reuse the semantic --*-bg tokens. */ -.modern-sk-callout{ display:flex; gap:12px; align-items:flex-start; padding:13px 15px; border-radius:var(--r-lg); border:1px solid var(--hair); background:var(--info-bg); color:var(--fg-1); box-shadow:var(--shadow-inset-well); } -.modern-sk-callout__icon{ flex-shrink:0; display:inline-flex; align-items:center; justify-content:center; width:26px; height:26px; border-radius:var(--r-sm); color:var(--info); } -.modern-sk-callout__body{ font-size:13px; line-height:1.5; color:var(--fg-2); padding-top:3px; } -.modern-sk-callout__body strong{ color:var(--fg-1); font-weight:600; } -.modern-sk-callout--success{ background:var(--success-bg); border-color:rgba(190,242,100,.22); } -.modern-sk-callout--success .modern-sk-callout__icon{ color:var(--success); } -.modern-sk-callout--warning{ background:var(--warning-bg); border-color:rgba(230,169,60,.22); } -.modern-sk-callout--warning .modern-sk-callout__icon{ color:var(--warning); } -.modern-sk-callout--danger{ background:var(--danger-bg); border-color:rgba(233,87,43,.25); } -.modern-sk-callout--danger .modern-sk-callout__icon{ color:var(--danger); } +.modern-sk-callout { + display: flex; + gap: 12px; + align-items: flex-start; + padding: 13px 15px; + border-radius: var(--r-lg); + border: 1px solid var(--hair); + background: var(--info-bg); + color: var(--fg-1); + box-shadow: var(--shadow-inset-well); +} +.modern-sk-callout__icon { + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + width: 26px; + height: 26px; + border-radius: var(--r-sm); + color: var(--info); +} +.modern-sk-callout__body { + font-size: 13px; + line-height: 1.5; + color: var(--fg-2); + padding-top: 3px; +} +.modern-sk-callout__body strong { + color: var(--fg-1); + font-weight: 600; +} +.modern-sk-callout--success { + background: var(--success-bg); + border-color: rgba(190, 242, 100, 0.22); +} +.modern-sk-callout--success .modern-sk-callout__icon { + color: var(--success); +} +.modern-sk-callout--warning { + background: var(--warning-bg); + border-color: rgba(230, 169, 60, 0.22); +} +.modern-sk-callout--warning .modern-sk-callout__icon { + color: var(--warning); +} +.modern-sk-callout--danger { + background: var(--danger-bg); + border-color: rgba(233, 87, 43, 0.25); +} +.modern-sk-callout--danger .modern-sk-callout__icon { + color: var(--danger); +} /* ---------- TABLE ---------- The list-row aesthetic stretched to columns: hairline grid, sunk header strip, mono numerals, lime row selection. */ -.modern-sk-table-wrap{ background:var(--steel-900); border:1px solid var(--hair); border-radius:var(--r-lg); box-shadow:var(--shadow-card); overflow:hidden; } -.modern-sk-table{ width:100%; border-collapse:collapse; font-size:14px; } -.modern-sk-table thead th{ text-align:left; font-family:var(--font-sans); font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.14em; color:var(--fg-3); padding:9px 14px; background:var(--grad-titlebar); border-bottom:1px solid var(--hair-strong); box-shadow:0 1px 0 rgba(255,255,255,.04) inset; white-space:nowrap; } -.modern-sk-table tbody td{ padding:10px 14px; border-bottom:1px solid var(--hair); color:var(--fg-1); vertical-align:middle; } -.modern-sk-table tbody tr:last-child td{ border-bottom:none; } -.modern-sk-table tbody tr:hover td{ background:rgba(255,255,255,.03); } -.modern-sk-table tbody tr.is-selected td{ background:linear-gradient(180deg,rgba(190,242,100,.16),rgba(190,242,100,.08)); } -.modern-sk-table .num{ font-family:var(--font-mono); font-size:12px; color:var(--fg-2); text-align:right; } -.modern-sk-table .muted{ color:var(--fg-3); } -[data-theme="light"] .modern-sk-table tbody tr:hover td{ background:rgba(0,0,0,.035); } +.modern-sk-table-wrap { + background: var(--steel-900); + border: 1px solid var(--hair); + border-radius: var(--r-lg); + box-shadow: var(--shadow-card); + overflow: hidden; +} +.modern-sk-table { + width: 100%; + border-collapse: collapse; + font-size: 14px; +} +.modern-sk-table thead th { + text-align: left; + font-family: var(--font-sans); + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--fg-3); + padding: 9px 14px; + background: var(--grad-titlebar); + border-bottom: 1px solid var(--hair-strong); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset; + white-space: nowrap; +} +.modern-sk-table tbody td { + padding: 10px 14px; + border-bottom: 1px solid var(--hair); + color: var(--fg-1); + vertical-align: middle; +} +.modern-sk-table tbody tr:last-child td { + border-bottom: none; +} +.modern-sk-table tbody tr:hover td { + background: rgba(255, 255, 255, 0.03); +} +.modern-sk-table tbody tr.is-selected td { + background: linear-gradient( + 180deg, + rgba(190, 242, 100, 0.16), + rgba(190, 242, 100, 0.08) + ); +} +.modern-sk-table .num { + font-family: var(--font-mono); + font-size: 12px; + color: var(--fg-2); + text-align: right; +} +.modern-sk-table .muted { + color: var(--fg-3); +} +[data-theme='light'] .modern-sk-table tbody tr:hover td { + background: rgba(0, 0, 0, 0.035); +} /* ---------- SCROLL AREA (Radix) ---------- */ -.modern-sk-scroll{ overflow:hidden; } -.modern-sk-scroll__viewport{ width:100%; height:100%; border-radius:inherit; } -.modern-sk-scroll__bar{ display:flex; user-select:none; touch-action:none; padding:2px; background:transparent; transition:background var(--dur-base) var(--ease-out); } -.modern-sk-scroll__bar[data-orientation="vertical"]{ width:10px; } -.modern-sk-scroll__bar[data-orientation="horizontal"]{ flex-direction:column; height:10px; } -.modern-sk-scroll:hover .modern-sk-scroll__bar{ background:rgba(0,0,0,.18); } -.modern-sk-scroll__thumb{ flex:1; background:var(--steel-500); border-radius:var(--r-pill); position:relative; box-shadow:0 1px 0 rgba(255,255,255,.08) inset; } -.modern-sk-scroll__thumb:hover{ background:var(--steel-400); } -.modern-sk-scroll__thumb::before{ content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); width:100%; height:100%; min-width:44px; min-height:44px; } +.modern-sk-scroll { + overflow: hidden; +} +.modern-sk-scroll__viewport { + width: 100%; + height: 100%; + border-radius: inherit; +} +.modern-sk-scroll__bar { + display: flex; + user-select: none; + touch-action: none; + padding: 2px; + background: transparent; + transition: background var(--dur-base) var(--ease-out); +} +.modern-sk-scroll__bar[data-orientation='vertical'] { + width: 10px; +} +.modern-sk-scroll__bar[data-orientation='horizontal'] { + flex-direction: column; + height: 10px; +} +.modern-sk-scroll:hover .modern-sk-scroll__bar { + background: rgba(0, 0, 0, 0.18); +} +.modern-sk-scroll__thumb { + flex: 1; + background: var(--steel-500); + border-radius: var(--r-pill); + position: relative; + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08) inset; +} +.modern-sk-scroll__thumb:hover { + background: var(--steel-400); +} +.modern-sk-scroll__thumb::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + height: 100%; + min-width: 44px; + min-height: 44px; +} /* ---------- DIALOG / MODAL (Radix Dialog + AlertDialog) ---------- The floating sheet: window chrome aesthetic, scrim with blur. */ -.modern-sk-overlay{ position:fixed; inset:0; z-index:80; background:rgba(8,9,6,.55); backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); } -.modern-sk-overlay[data-state="open"]{ animation:modern-sk-fade-in var(--dur-base) var(--ease-out); } -.modern-sk-overlay[data-state="closed"]{ animation:modern-sk-fade-out var(--dur-quick) var(--ease-out); } -.modern-sk-dialog{ position:fixed; z-index:81; top:50%; left:50%; translate:-50% -50%; width:min(92vw,460px); max-height:85vh; overflow:auto; border-radius:var(--r-xl); border:1px solid var(--hair-strong); background:var(--steel-800); box-shadow:var(--shadow-window); padding:22px 22px 20px; transform-origin:center; font-family:var(--font-sans); } -.modern-sk-dialog[data-state="open"]{ animation:modern-sk-scale-in var(--dur-base) var(--ease-out); } -.modern-sk-dialog[data-state="closed"]{ animation:modern-sk-scale-out var(--dur-quick) var(--ease-out); } -.modern-sk-dialog__title{ font-family:var(--font-sans); font-size:var(--text-lg); font-weight:600; color:var(--fg-1); letter-spacing:var(--track-snug); } -.modern-sk-dialog__desc{ margin-top:8px; font-size:14px; line-height:1.55; color:var(--fg-2); } -.modern-sk-dialog__body{ margin-top:16px; } -.modern-sk-dialog__footer{ display:flex; justify-content:flex-end; gap:12px; margin-top:22px; } -.modern-sk-dialog__close{ position:absolute; top:14px; right:14px; } +.modern-sk-overlay { + position: fixed; + inset: 0; + z-index: 80; + background: rgba(8, 9, 6, 0.55); + backdrop-filter: blur(3px); + -webkit-backdrop-filter: blur(3px); +} +.modern-sk-overlay[data-state='open'] { + animation: modern-sk-fade-in var(--dur-base) var(--ease-out); +} +.modern-sk-overlay[data-state='closed'] { + animation: modern-sk-fade-out var(--dur-quick) var(--ease-out); +} +.modern-sk-dialog { + position: fixed; + z-index: 81; + top: 50%; + left: 50%; + translate: -50% -50%; + width: min(92vw, 460px); + max-height: 85vh; + overflow: auto; + border-radius: var(--r-xl); + border: 1px solid var(--hair-strong); + background: var(--steel-800); + box-shadow: var(--shadow-window); + padding: 22px 22px 20px; + transform-origin: center; + font-family: var(--font-sans); +} +.modern-sk-dialog[data-state='open'] { + animation: modern-sk-scale-in var(--dur-base) var(--ease-out); +} +.modern-sk-dialog[data-state='closed'] { + animation: modern-sk-scale-out var(--dur-quick) var(--ease-out); +} +.modern-sk-dialog__title { + font-family: var(--font-sans); + font-size: var(--text-lg); + font-weight: 600; + color: var(--fg-1); + letter-spacing: var(--track-snug); +} +.modern-sk-dialog__desc { + margin-top: 8px; + font-size: 14px; + line-height: 1.55; + color: var(--fg-2); +} +.modern-sk-dialog__body { + margin-top: 16px; +} +.modern-sk-dialog__footer { + display: flex; + justify-content: flex-end; + gap: 12px; + margin-top: 22px; +} +.modern-sk-dialog__close { + position: absolute; + top: 14px; + right: 14px; +} /* ============================================================ LIGHT THEME — component overrides (ported 1:1) ============================================================ */ -[data-theme="light"] .modern-sk-btn--ghost:hover{ background:rgba(0,0,0,.05); color:var(--fg-1); } -[data-theme="light"] .modern-sk-btn--ghost:active{ background:rgba(0,0,0,.08); } -[data-theme="light"] .modern-sk-row:hover{ background:rgba(0,0,0,.035); } - -[data-theme="light"] .modern-sk-btn--ember{ - box-shadow:0 1px 0 rgba(255,255,255,.25) inset, 0 1px 2px rgba(0,0,0,.18), 0 6px 14px rgba(233,87,43,.22); +[data-theme='light'] .modern-sk-btn--ghost:hover { + background: rgba(0, 0, 0, 0.05); + color: var(--fg-1); +} +[data-theme='light'] .modern-sk-btn--ghost:active { + background: rgba(0, 0, 0, 0.08); +} +[data-theme='light'] .modern-sk-row:hover { + background: rgba(0, 0, 0, 0.035); } -[data-theme="light"] .modern-sk-switch__thumb{ - background:linear-gradient(180deg,#ffffff,#e7e7dd); - box-shadow:0 1px 2px rgba(0,0,0,.22), 0 1px 0 rgba(255,255,255,.9) inset; +[data-theme='light'] .modern-sk-btn--ember { + box-shadow: + 0 1px 0 rgba(255, 255, 255, 0.25) inset, + 0 1px 2px rgba(0, 0, 0, 0.18), + 0 6px 14px rgba(233, 87, 43, 0.22); } -[data-theme="light"] .modern-sk-switch{ background:var(--steel-600); } -[data-theme="light"] .modern-sk-switch[data-state="checked"]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); } -[data-theme="light"] .modern-sk-slider__thumb{ box-shadow:0 1px 3px rgba(0,0,0,.28), 0 1px 0 rgba(255,255,255,.95) inset, 0 0 0 .5px rgba(0,0,0,.06); } +[data-theme='light'] .modern-sk-switch__thumb { + background: linear-gradient(180deg, #ffffff, #e7e7dd); + box-shadow: + 0 1px 2px rgba(0, 0, 0, 0.22), + 0 1px 0 rgba(255, 255, 255, 0.9) inset; +} +[data-theme='light'] .modern-sk-switch { + background: var(--steel-600); +} +[data-theme='light'] .modern-sk-switch[data-state='checked'] { + background: linear-gradient(180deg, var(--lime), var(--lime-deep)); +} -[data-theme="light"] .modern-sk-menu{ background:rgba(246,246,239,.82); border-color:var(--hair-strong); } -[data-theme="light"] .modern-sk-select__content{ background:rgba(246,246,239,.82); } -[data-theme="light"] .modern-sk-tooltip{ background:rgba(246,246,239,.94); color:var(--fg-1); } +[data-theme='light'] .modern-sk-slider__thumb { + box-shadow: + 0 1px 3px rgba(0, 0, 0, 0.28), + 0 1px 0 rgba(255, 255, 255, 0.95) inset, + 0 0 0 0.5px rgba(0, 0, 0, 0.06); +} -[data-theme="light"] .modern-sk-titlebar{ border-bottom-color:rgba(0,0,0,.12); } -[data-theme="light"] .modern-sk-traffic{ box-shadow:0 1px 1px rgba(0,0,0,.18) inset, 0 0 0 .5px rgba(0,0,0,.14); } +[data-theme='light'] .modern-sk-menu { + background: rgba(246, 246, 239, 0.82); + border-color: var(--hair-strong); +} +[data-theme='light'] .modern-sk-select__content { + background: rgba(246, 246, 239, 0.82); +} +[data-theme='light'] .modern-sk-tooltip { + background: rgba(246, 246, 239, 0.94); + color: var(--fg-1); +} -[data-theme="light"] .modern-sk-overlay{ background:rgba(60,62,52,.32); } -[data-theme="light"] .modern-sk-scroll:hover .modern-sk-scroll__bar{ background:rgba(0,0,0,.06); } -[data-theme="light"] .modern-sk-table thead th{ box-shadow:0 1px 0 rgba(255,255,255,.6) inset; } +[data-theme='light'] .modern-sk-titlebar { + border-bottom-color: rgba(0, 0, 0, 0.12); +} +[data-theme='light'] .modern-sk-traffic { + box-shadow: + 0 1px 1px rgba(0, 0, 0, 0.18) inset, + 0 0 0 0.5px rgba(0, 0, 0, 0.14); +} + +[data-theme='light'] .modern-sk-overlay { + background: rgba(60, 62, 52, 0.32); +} +[data-theme='light'] .modern-sk-scroll:hover .modern-sk-scroll__bar { + background: rgba(0, 0, 0, 0.06); +} +[data-theme='light'] .modern-sk-table thead th { + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.6) inset; +} diff --git a/src/styles/index.css b/src/styles/index.css index 9334283..2334e91 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -7,7 +7,13 @@ @import './tokens.css'; @import './components.css'; -/* Non-invasive box-sizing for our own components only (zero specificity). */ +/* Non-invasive box-sizing + branded font for our own components only + (zero specificity, so consumer elements are never touched and the + --font-mono / --font-display classes still win by class specificity). + This is what carries the typeface onto portalled content (tooltips, + menus, dialogs) and bare text nodes (control labels) that never set + their own font-family. */ :where([class^='modern-sk-'], [class*=' modern-sk-']) { box-sizing: border-box; + font-family: var(--font-sans); } diff --git a/src/styles/tokens.css b/src/styles/tokens.css index 281aa46..899aa7c 100644 --- a/src/styles/tokens.css +++ b/src/styles/tokens.css @@ -178,10 +178,9 @@ --grain-opacity: 0.45; /* ---------- SPINNER GROOVE ---------- - Carved donut channel — dark at the top rim, catching light at the - bottom, exactly like the sunk wells (switch / field). */ - --spin-groove-1: #090a07; /* top — in shadow */ - --spin-groove-2: #34352b; /* bottom — catches light */ + Solid carved channel — flat base felt, sunk by an SVG inner shadow, + exactly like the switch well (no gradient). */ + --spin-track: #23241c; } /* ============================================================ @@ -264,9 +263,8 @@ radial-gradient(90% 70% at 85% 110%, rgba(233,87,43,0.08), transparent 60%), radial-gradient(100% 100% at 50% 50%, #f2f2ea 0%, #ecece3 60%, #e2e2d6 100%); - /* carved groove on warm paper — top grey shadow, bottom near-white */ - --spin-groove-1: #c2c3b6; - --spin-groove-2: #ffffff; + /* carved groove on warm paper — flat base, sunk by inner shadow */ + --spin-track: #dcdcd2; } /* ============================================================