Files
modern-sk/src/styles/components.css
T
2026-06-05 15:11:02 +03:00

1652 lines
41 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.
/* ============================================================
ModernSK — Components layer (ported to Radix Primitives)
Visual recipes kept 1:1 from the Claude Design bundle; state
hooks rewired from .is-* classes to Radix data-attributes
([data-state], [data-highlighted], [data-disabled]).
============================================================ */
@keyframes modern-sk-pop {
from {
opacity: 0;
transform: scale(0.6);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes modern-sk-scale-in {
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(0.96);
}
}
@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: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: 0.4;
filter: saturate(0.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, 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::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;
}
/* ---------- TYPING ANIMATION (osu!-lazer style char in/out) ---------- */
/* The real field renders transparent over a mirrored per-letter overlay. */
.modern-sk-field-wrap {
position: relative;
width: 100%;
}
.modern-sk-search .modern-sk-field-wrap {
width: 100%;
}
.modern-sk-field--animated {
color: transparent;
caret-color: var(--fg-1);
}
.modern-sk-field--animated::placeholder {
color: var(--fg-3);
}
.modern-sk-field-overlay {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
/* transparent border mirrors the field's 1px border so content aligns */
border: 1px solid transparent;
padding: var(--field-pad-y) var(--field-pad-x);
font-family: var(--font-sans);
font-size: 14px;
line-height: normal;
color: var(--fg-1);
white-space: pre;
text-align: left;
will-change: transform;
}
.modern-sk-field-overlay--multiline {
white-space: pre-wrap;
overflow-wrap: break-word;
line-height: 1.5;
}
.modern-sk-search .modern-sk-field-overlay {
padding-left: 34px;
}
.modern-sk-field-char {
/* inline (not inline-block) so the overlay wraps exactly like the field;
inherit white-space so multiline (pre-wrap) wraps while input (pre) won't.
'pre' on the span itself would suppress wrapping at its boundaries.
Multiline keeps this inline path: inline boxes ignore transform, so the
rise eases `top` (a layout property — acceptable here, textarea wraps
instead of scrolling so there's no per-keystroke scroll-sync thrash). */
position: relative;
white-space: inherit;
animation: modern-sk-char-in var(--dur-base) var(--ease-snap) both;
}
/* Single-line never wraps, so chars can be inline-block and animate
`transform` (compositor-only) instead of `top` (relayout every frame).
The single-line field scrolls horizontally and syncs the overlay each
keystroke, so the layout-triggering `top` path janked there — this doesn't. */
.modern-sk-field-char--composited {
display: inline-block;
will-change: transform, opacity;
animation-name: modern-sk-char-in-rise;
}
.modern-sk-field-char--leaving {
display: inline-block;
position: absolute;
white-space: pre;
animation: modern-sk-char-out var(--dur-base) var(--ease-out) forwards;
}
/* inline elements ignore transform, so the rise uses top instead */
@keyframes modern-sk-char-in {
from {
opacity: 0;
top: -0.32em;
}
to {
opacity: 1;
top: 0;
}
}
/* inline-block (single-line) variant — composited transform, no relayout */
@keyframes modern-sk-char-in-rise {
from {
opacity: 0;
transform: translateY(-0.32em);
}
to {
opacity: 1;
transform: none;
}
}
@keyframes modern-sk-char-out {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(0.7em) scale(0.9);
}
}
@media (prefers-reduced-motion: reduce) {
.modern-sk-field-char {
animation: none;
}
.modern-sk-field-char--leaving {
animation: modern-sk-char-out 1ms linear forwards;
}
}
/* ---------- 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: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, 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;
}
/* ---------- 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__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);
}
/* ---------- 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[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);
}
/* ---------- 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__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);
}
/* control + label row helper */
.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);
}
/* ---------- SLIDER ---------- */
.modern-sk-slider {
--ms-thumb: 20px;
--ms-thumb-w: var(--ms-thumb);
position: relative;
display: flex;
align-items: center;
width: 100%;
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);
}
/* Radix positions the range via left/right offsets (not width); ease those
so the fill glides between discrete steps while dragging. */
.modern-sk-slider--animated .modern-sk-slider__range {
transition:
left 0.12s ease-out,
right 0.12s ease-out;
}
.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;
}
/* Radix sets the step position (left) on a WRAPPER span around the thumb, not on
the thumb element itself — so the transition must live on that wrapper. The
wrapper is the slider's direct child span that isn't the track. */
.modern-sk-slider--animated > span:not(.modern-sk-slider__track) {
transition: left 0.12s ease-out;
}
@media (prefers-reduced-motion: reduce) {
.modern-sk-slider--animated > span:not(.modern-sk-slider__track),
.modern-sk-slider--animated .modern-sk-slider__range {
transition: 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;
}
/* ---------- KNOB (circular slider) ----------
Skeuomorphic rotary control. The dial follows the pointer 1:1 while dragging
(bound to the mouse); the gauge fill + value snap to detents and *glide*
between them (like the stepped Slider) when `--animated`. Anatomy:
recessed SVG gauge ring → glossy cap → knurled dial (rotates, carries the
accent pointer) → turned-metal hub. Sizing flows from --knob-size. */
.modern-sk-knob {
--knob-size: 108px;
--knob-accent: var(--lime);
--knob-accent-deep: var(--lime-deep);
--knob-glow: rgba(190, 242, 100, 0.5);
position: relative;
flex-shrink: 0;
width: var(--knob-size);
height: var(--knob-size);
cursor: grab;
outline: none;
touch-action: none;
-webkit-tap-highlight-color: transparent;
}
.modern-sk-knob.is-dragging {
cursor: grabbing;
}
.modern-sk-knob--disabled {
cursor: default;
opacity: 0.55;
}
/* recessed gauge ring */
.modern-sk-knob__gauge {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
overflow: visible;
pointer-events: none;
}
.modern-sk-knob__track {
fill: none;
stroke: var(--steel-800);
stroke-width: 5;
stroke-linecap: round;
}
/* The fill is the full arc, revealed up to the value via stroke-dashoffset (set
inline as a plain number). When `--animated`, transition the offset so the
accent eases along the circle between detents. */
.modern-sk-knob__fill {
fill: none;
stroke: var(--knob-accent);
stroke-width: 5;
stroke-linecap: round;
filter: drop-shadow(0 0 5px var(--knob-glow));
transition: stroke var(--dur-quick) var(--ease-out);
}
.modern-sk-knob--animated .modern-sk-knob__fill {
transition:
stroke-dashoffset 0.12s ease-out,
stroke var(--dur-quick) var(--ease-out);
}
.modern-sk-knob__tick {
stroke: var(--fg-3);
stroke-width: 1.6;
stroke-linecap: round;
opacity: 0.45;
transition:
stroke var(--dur-quick),
opacity var(--dur-quick);
}
.modern-sk-knob__tick.is-on {
stroke: var(--knob-accent);
opacity: 0.95;
}
/* glossy cap — static top-down gloss (light source stays put) */
.modern-sk-knob__cap {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(var(--knob-size) - 34px);
height: calc(var(--knob-size) - 34px);
border-radius: 50%;
border: 1px solid var(--hair-strong);
background:
radial-gradient(circle at 50% 30%, rgba(255, 255, 255, 0.16), transparent 58%),
var(--grad-key);
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.08) inset,
0 4px 9px rgba(0, 0, 0, 0.5),
0 11px 22px rgba(0, 0, 0, 0.42);
overflow: hidden;
transition: box-shadow var(--dur-quick) var(--ease-out);
}
/* knurled dial — rotates with the pointer, carries the glowing pointer dot.
The ribbed grip lives on ::before (masked to a ring) so the pointer dot can
sit unmasked on top and keep its full halo. While dragging it tracks the
mouse with no transition; otherwise it glides to the settled value. */
.modern-sk-knob__dial {
position: absolute;
inset: 0;
border-radius: 50%;
will-change: transform;
}
.modern-sk-knob--animated .modern-sk-knob__dial {
transition: transform 0.12s ease-out;
}
.modern-sk-knob.is-dragging .modern-sk-knob__dial {
transition: none;
}
.modern-sk-knob__dial::before {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
background: repeating-conic-gradient(
from 0deg,
rgba(255, 255, 255, 0.09) 0deg 3deg,
rgba(0, 0, 0, 0.28) 3deg 6deg
);
-webkit-mask: radial-gradient(circle, transparent 0 58%, #000 61% 100%);
mask: radial-gradient(circle, transparent 0 58%, #000 61% 100%);
}
.modern-sk-knob__pointer {
position: absolute;
top: 5%;
left: 50%;
transform: translateX(-50%);
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--knob-accent);
box-shadow: 0 0 6px var(--knob-glow);
}
/* turned-metal hub covers the inner ends; pointer reads as an outer notch */
.modern-sk-knob__hub {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 48%;
height: 48%;
border-radius: 50%;
background: radial-gradient(circle at 50% 34%, #36372c, #1d1e17);
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.07) inset,
0 1px 3px rgba(0, 0, 0, 0.55);
}
.modern-sk-knob:focus-visible .modern-sk-knob__cap {
box-shadow:
var(--focus-ring),
0 1px 0 rgba(255, 255, 255, 0.08) inset,
0 4px 9px rgba(0, 0, 0, 0.5),
0 11px 22px rgba(0, 0, 0, 0.42);
}
/* ember accent variant */
.modern-sk-knob--ember {
--knob-accent: var(--ember);
--knob-accent-deep: var(--ember-deep);
--knob-glow: rgba(233, 87, 43, 0.5);
}
@media (prefers-reduced-motion: reduce) {
.modern-sk-knob--animated .modern-sk-knob__fill,
.modern-sk-knob--animated .modern-sk-knob__dial {
transition: none;
}
}
/* light theme: brighter cap gloss, darker knurl ridges so the grip reads, a
pale turned hub, and a lighter recessed gauge channel. */
[data-theme='light'] .modern-sk-knob__cap {
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.9) inset,
0 2px 5px rgba(0, 0, 0, 0.16),
0 7px 16px rgba(0, 0, 0, 0.12);
}
[data-theme='light'] .modern-sk-knob__dial::before {
background: repeating-conic-gradient(
from 0deg,
rgba(0, 0, 0, 0.09) 0deg 3deg,
rgba(255, 255, 255, 0.6) 3deg 6deg
);
}
[data-theme='light'] .modern-sk-knob__hub {
background: radial-gradient(circle at 50% 34%, #ffffff, #e2e2d6);
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.9) inset,
0 1px 3px rgba(0, 0, 0, 0.16);
}
[data-theme='light'] .modern-sk-knob__track {
stroke: var(--steel-500);
}
[data-theme='light'] .modern-sk-knob__tick {
stroke: var(--steel-400);
}
/* ---------- 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);
}
/* ---------- 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: 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);
}
/* ---------- 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;
}
/* ---------- 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;
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, 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, 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, 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, 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, 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;
}
/* ---------- SPINNER ----------
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;
}
}
/* ---------- CALLOUT ----------
Soft semantic surface (1214% 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, 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: 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, 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, 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, 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-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__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-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-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-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;
}