stuff
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## What this is
|
||||||
|
|
||||||
|
`@modernsk/ui` — a tactile, dark-first React component library built on [Radix](https://www.radix-ui.com/) primitives. Distributed as a git-installable / publishable package; only `dist/` ships. Consumers get built ESM + CJS, `.d.ts` types, and a single `styles.css`.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev # Rsbuild playground at http://localhost:3000 (renders src/App.tsx — every component on one page)
|
||||||
|
npm run build # build publishable package: tsup (JS/types) + build:css (bundled stylesheet) into dist/
|
||||||
|
npm run lint # rslint (rslint.config.ts)
|
||||||
|
npm run format # prettier --write .
|
||||||
|
```
|
||||||
|
|
||||||
|
No test suite exists. `npm run build` runs automatically on install via the `prepare` script — consumers build the package themselves.
|
||||||
|
|
||||||
|
The build is two steps that must both run (the `build` script chains them):
|
||||||
|
- `tsup` bundles `src/index.ts` → ESM/CJS + types. `react`/`react-dom` are externalized (peer deps); `radix-ui` + `@phosphor-icons/react` are bundled.
|
||||||
|
- `build:css` runs esbuild on `src/styles/index.css` with `--loader:.ttf=dataurl`, inlining the self-hosted Anta font so no asset hosting is needed.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Two parallel surfaces share the same source but never mix at publish time:
|
||||||
|
|
||||||
|
- **Library (shipped):** `src/index.ts` is the public entry. It re-exports everything from `src/components/ui.tsx`, plus `ThemeProvider`/`useTheme` from `src/components/theme.tsx`, and exposes `TooltipProvider` (Radix's `Tooltip.Provider`). The shipped stylesheet entry is `src/styles/index.css`.
|
||||||
|
- **Playground (dev-only, never published):** `src/index.tsx` mounts `src/App.tsx`, and uses `src/styles/global.css`. These are the Rsbuild dev target only.
|
||||||
|
|
||||||
|
`src/styles/index.css` (shipped) vs `src/styles/global.css` (dev) is a deliberate split: `index.css` imports only `tokens.css` + `components.css` and applies box-sizing at **zero specificity** via `:where([class^='msk-'])` so it never touches consumer elements. `global.css` adds a global reset and kitchen-sink layout helpers — those must stay out of the shipped bundle.
|
||||||
|
|
||||||
|
### Components (`src/components/ui.tsx`)
|
||||||
|
|
||||||
|
All components live in one file. Pattern: Radix provides logic/accessibility, every visual comes from CSS. Components are thin wrappers that attach `msk-*` classes and spread props. The `cx()` helper joins class names (no classnames dependency). Components forward refs where they wrap a DOM element. There is no inline styling and no CSS-in-JS — **all appearance is driven by `msk-*` classes resolving against CSS custom properties.**
|
||||||
|
|
||||||
|
### Styling system (`src/styles/`)
|
||||||
|
|
||||||
|
- `tokens.css` — single source of truth: color/type CSS custom properties, `@font-face`, Google Fonts import. Every component reads from here.
|
||||||
|
- `components.css` — the `msk-*` class definitions.
|
||||||
|
- Dark/light is driven by `data-theme` on `<html>`, set by `ThemeProvider` (persisted to `localStorage` under key `msk-theme`, default `dark`).
|
||||||
|
|
||||||
|
When adding or changing a component: add the wrapper in `ui.tsx`, define its `msk-*` styles in `components.css`, and pull any new color/spacing value from a token in `tokens.css` rather than hardcoding.
|
||||||
|
|
||||||
|
## Consumer contract
|
||||||
|
|
||||||
|
Consumers import `@modernsk/ui/styles.css` once at app root, wrap their tree in `ThemeProvider`, and wrap any tooltip-using subtree in `TooltipProvider`. Keep these provider requirements intact when refactoring exports.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- React 19, React Compiler is enabled (`babel-plugin-react-compiler` in the Rsbuild dev pipeline).
|
||||||
|
- TypeScript is `noEmit` + `verbatimModuleSyntax`: use `import type` for type-only imports. `noUnusedLocals`/`noUnusedParameters` are on.
|
||||||
|
- ESM-only package (`"type": "module"`).
|
||||||
@@ -1,14 +1,21 @@
|
|||||||
# @modernsk/ui
|
<div align="center">
|
||||||
|
|
||||||
|
<img src="assets/logo.svg" alt="ModernSK" width="380" />
|
||||||
|
|
||||||
|
**Tactile, dark-first React component library built on [Radix](https://www.radix-ui.com/) primitives.**
|
||||||
|
|
||||||
Tactile, dark-first React component library built on [Radix](https://www.radix-ui.com/) primitives.
|
|
||||||
Old-iOS skeuomorphism × macOS Sequoia neatness × Ubuntu warmth.
|
Old-iOS skeuomorphism × macOS Sequoia neatness × Ubuntu warmth.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
Git-hosted (pin to a tag):
|
Distributed via self-hosted git — install straight from the repo:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install github:YOUR_ORG/modernsk-ui#v0.1.0
|
npm i git+ssh://git@git.ollyhearn.ru:49239/olly/modern-sk.git
|
||||||
```
|
```
|
||||||
|
|
||||||
`react` and `react-dom` (>=18) are peer dependencies — your app provides them.
|
`react` and `react-dom` (>=18) are peer dependencies — your app provides them.
|
||||||
@@ -19,8 +26,9 @@ The package builds itself on install via the `prepare` script.
|
|||||||
Import the stylesheet once at your app root, then use components anywhere:
|
Import the stylesheet once at your app root, then use components anywhere:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import '@modernsk/ui/styles.css';
|
import 'modern-sk/styles.css'; // required — tokens + components
|
||||||
import { ThemeProvider, TooltipProvider, Button, Card } from '@modernsk/ui';
|
import 'modern-sk/fonts.css'; // optional — branded faces (see Fonts)
|
||||||
|
import { ThemeProvider, TooltipProvider, Button, Card } from 'modern-sk';
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
return (
|
return (
|
||||||
@@ -37,7 +45,31 @@ export function App() {
|
|||||||
|
|
||||||
- `ThemeProvider` manages dark/light via `data-theme` on `<html>` and persists to `localStorage`. Read it with `useTheme()`.
|
- `ThemeProvider` manages dark/light via `data-theme` on `<html>` and persists to `localStorage`. Read it with `useTheme()`.
|
||||||
- `TooltipProvider` must wrap any tree that uses `<Tooltip>`.
|
- `TooltipProvider` must wrap any tree that uses `<Tooltip>`.
|
||||||
- All visuals come from CSS custom properties in the shipped stylesheet; the self-hosted display font is inlined, so no extra asset hosting is needed.
|
- All visuals come from CSS custom properties in the shipped stylesheet.
|
||||||
|
|
||||||
|
## Fonts
|
||||||
|
|
||||||
|
`modern-sk/styles.css` ships **no fonts**. The type tokens default to a chain
|
||||||
|
that degrades to `system-ui`, so the library works with zero font loading.
|
||||||
|
|
||||||
|
To get the branded ModernSK faces (Anta display + Onest + Geist Mono), import
|
||||||
|
the optional stylesheet — Anta is self-hosted and inlined, no asset hosting
|
||||||
|
needed:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import 'modern-sk/fonts.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
To use your **own** fonts, skip `fonts.css` and override the tokens anywhere
|
||||||
|
after `styles.css` — every component re-reads them:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--font-display: 'Your Display', sans-serif;
|
||||||
|
--font-sans: 'Inter', system-ui, sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Develop
|
## Develop
|
||||||
|
|
||||||
@@ -51,4 +83,14 @@ npm run lint
|
|||||||
|
|
||||||
## What ships
|
## What ships
|
||||||
|
|
||||||
`npm publish` / git-install exposes only `dist/` — built ESM + CJS, `.d.ts` types, and `styles.css`. The playground (`src/App.tsx`, Rsbuild config) is dev-only and never published.
|
A git install exposes only `dist/` — built ESM + CJS, `.d.ts` types,
|
||||||
|
`styles.css`, and `fonts.css`. The playground (`src/App.tsx`, Rsbuild config)
|
||||||
|
is dev-only and never published.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
brought to you by **ollyhearn** & **claude** with <3
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<svg width="440" height="140" viewBox="0 0 440 140" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="ModernSK">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="panel" x1="0" y1="0" x2="0" y2="140" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#1b1d15"/>
|
||||||
|
<stop offset="1" stop-color="#0d0e0a"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="sk" x1="300" y1="0" x2="420" y2="140" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#d4ff7a"/>
|
||||||
|
<stop offset="1" stop-color="#a8e04a"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect x="1" y="1" width="438" height="138" rx="22" fill="url(#panel)" stroke="#2a2c22" stroke-width="1.5"/>
|
||||||
|
<rect x="6" y="6" width="428" height="2" rx="1" fill="#ffffff" opacity="0.05"/>
|
||||||
|
|
||||||
|
<text x="50%" y="50%" dominant-baseline="central" text-anchor="middle"
|
||||||
|
font-family="'Anta', 'Onest', system-ui, -apple-system, 'Segoe UI', sans-serif"
|
||||||
|
font-size="58" font-weight="700" letter-spacing="2">
|
||||||
|
<tspan fill="#e7e8e0">MODERN</tspan><tspan fill="url(#sk)">SK</tspan>
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
Generated
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@modernsk/ui",
|
"name": "modern-sk",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@modernsk/ui",
|
"name": "modern-sk",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+4
-3
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "@modernsk/ui",
|
"name": "modern-sk",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "ModernSK — tactile, dark-first React component library built on Radix primitives.",
|
"description": "ModernSK — tactile, dark-first React component library built on Radix primitives.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -16,14 +16,15 @@
|
|||||||
"import": "./dist/index.js",
|
"import": "./dist/index.js",
|
||||||
"require": "./dist/index.cjs"
|
"require": "./dist/index.cjs"
|
||||||
},
|
},
|
||||||
"./styles.css": "./dist/styles.css"
|
"./styles.css": "./dist/styles.css",
|
||||||
|
"./fonts.css": "./dist/fonts.css"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup && npm run build:css",
|
"build": "tsup && npm run build:css",
|
||||||
"build:css": "esbuild src/styles/index.css --bundle --loader:.ttf=dataurl --outfile=dist/styles.css",
|
"build:css": "esbuild src/styles/index.css --bundle --outfile=dist/styles.css && esbuild src/styles/fonts.css --bundle --loader:.ttf=dataurl --outfile=dist/fonts.css",
|
||||||
"dev": "rsbuild dev --open",
|
"dev": "rsbuild dev --open",
|
||||||
"preview": "rsbuild preview",
|
"preview": "rsbuild preview",
|
||||||
"lint": "rslint",
|
"lint": "rslint",
|
||||||
|
|||||||
+4
-4
@@ -76,7 +76,7 @@ const App = () => {
|
|||||||
const [chips, setChips] = useState(['design', '2026', 'invoices']);
|
const [chips, setChips] = useState(['design', '2026', 'invoices']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="msk-felt">
|
<div className="modern-sk-felt">
|
||||||
<div className="wrap">
|
<div className="wrap">
|
||||||
<header>
|
<header>
|
||||||
<div className="topbar">
|
<div className="topbar">
|
||||||
@@ -87,8 +87,8 @@ const App = () => {
|
|||||||
<p className="sub">
|
<p className="sub">
|
||||||
Every component, live and interactive, built on Radix Primitives
|
Every component, live and interactive, built on Radix Primitives
|
||||||
styled from the global tokens in{' '}
|
styled from the global tokens in{' '}
|
||||||
<span className="msk-mono">tokens.css</span> +{' '}
|
<span className="modern-sk-mono">tokens.css</span> +{' '}
|
||||||
<span className="msk-mono">components.css</span>. Click, toggle,
|
<span className="modern-sk-mono">components.css</span>. Click, toggle,
|
||||||
focus — it all responds.
|
focus — it all responds.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -242,7 +242,7 @@ const App = () => {
|
|||||||
onDecrement={() => setCount((n) => Math.max(0, n - 1))}
|
onDecrement={() => setCount((n) => Math.max(0, n - 1))}
|
||||||
onIncrement={() => setCount((n) => n + 1)}
|
onIncrement={() => setCount((n) => n + 1)}
|
||||||
/>
|
/>
|
||||||
<span className="msk-mono" style={{ color: 'var(--fg-2)' }}>
|
<span className="modern-sk-mono" style={{ color: 'var(--fg-2)' }}>
|
||||||
{count}
|
{count}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
type ThemeMode = 'dark' | 'light';
|
type ThemeMode = 'dark' | 'light';
|
||||||
const KEY = 'msk-theme';
|
const KEY = 'modern-sk-theme';
|
||||||
|
|
||||||
const ThemeContext = createContext<{
|
const ThemeContext = createContext<{
|
||||||
theme: ThemeMode;
|
theme: ThemeMode;
|
||||||
|
|||||||
+78
-78
@@ -42,10 +42,10 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|||||||
<button
|
<button
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cx(
|
className={cx(
|
||||||
'msk-btn',
|
'modern-sk-btn',
|
||||||
variant !== 'key' && `msk-btn--${variant}`,
|
variant !== 'key' && `modern-sk-btn--${variant}`,
|
||||||
size === 'sm' && 'msk-btn--sm',
|
size === 'sm' && 'modern-sk-btn--sm',
|
||||||
iconOnly && 'msk-btn--icon',
|
iconOnly && 'modern-sk-btn--icon',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -59,7 +59,7 @@ export const TextField = forwardRef<
|
|||||||
HTMLInputElement,
|
HTMLInputElement,
|
||||||
ComponentPropsWithoutRef<'input'>
|
ComponentPropsWithoutRef<'input'>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<input ref={ref} className={cx('msk-field', className)} {...props} />
|
<input ref={ref} className={cx('modern-sk-field', className)} {...props} />
|
||||||
));
|
));
|
||||||
TextField.displayName = 'TextField';
|
TextField.displayName = 'TextField';
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ export const TextArea = forwardRef<
|
|||||||
HTMLTextAreaElement,
|
HTMLTextAreaElement,
|
||||||
ComponentPropsWithoutRef<'textarea'>
|
ComponentPropsWithoutRef<'textarea'>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<textarea ref={ref} className={cx('msk-field', className)} {...props} />
|
<textarea ref={ref} className={cx('modern-sk-field', className)} {...props} />
|
||||||
));
|
));
|
||||||
TextArea.displayName = 'TextArea';
|
TextArea.displayName = 'TextArea';
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ export const SearchField = ({
|
|||||||
icon,
|
icon,
|
||||||
...props
|
...props
|
||||||
}: ComponentPropsWithoutRef<'input'> & { icon: ReactNode }) => (
|
}: ComponentPropsWithoutRef<'input'> & { icon: ReactNode }) => (
|
||||||
<div className="msk-search">
|
<div className="modern-sk-search">
|
||||||
<span className="ph">{icon}</span>
|
<span className="ph">{icon}</span>
|
||||||
<TextField {...props} />
|
<TextField {...props} />
|
||||||
</div>
|
</div>
|
||||||
@@ -103,15 +103,15 @@ export const Select = ({
|
|||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
onValueChange={onValueChange}
|
onValueChange={onValueChange}
|
||||||
>
|
>
|
||||||
<RSelect.Trigger className="msk-select" aria-label={rest['aria-label']}>
|
<RSelect.Trigger className="modern-sk-select" aria-label={rest['aria-label']}>
|
||||||
<RSelect.Value placeholder={placeholder} />
|
<RSelect.Value placeholder={placeholder} />
|
||||||
<RSelect.Icon className="msk-select__icon">
|
<RSelect.Icon className="modern-sk-select__icon">
|
||||||
<CaretDown size={12} weight="bold" />
|
<CaretDown size={12} weight="bold" />
|
||||||
</RSelect.Icon>
|
</RSelect.Icon>
|
||||||
</RSelect.Trigger>
|
</RSelect.Trigger>
|
||||||
<RSelect.Portal>
|
<RSelect.Portal>
|
||||||
<RSelect.Content
|
<RSelect.Content
|
||||||
className="msk-select__content"
|
className="modern-sk-select__content"
|
||||||
position="popper"
|
position="popper"
|
||||||
sideOffset={6}
|
sideOffset={6}
|
||||||
>
|
>
|
||||||
@@ -120,10 +120,10 @@ export const Select = ({
|
|||||||
<RSelect.Item
|
<RSelect.Item
|
||||||
key={it.value}
|
key={it.value}
|
||||||
value={it.value}
|
value={it.value}
|
||||||
className="msk-select__item"
|
className="modern-sk-select__item"
|
||||||
>
|
>
|
||||||
<RSelect.ItemText>{it.label}</RSelect.ItemText>
|
<RSelect.ItemText>{it.label}</RSelect.ItemText>
|
||||||
<RSelect.ItemIndicator className="msk-select__item-indicator">
|
<RSelect.ItemIndicator className="modern-sk-select__item-indicator">
|
||||||
<Check size={14} weight="bold" />
|
<Check size={14} weight="bold" />
|
||||||
</RSelect.ItemIndicator>
|
</RSelect.ItemIndicator>
|
||||||
</RSelect.Item>
|
</RSelect.Item>
|
||||||
@@ -138,8 +138,8 @@ export const Select = ({
|
|||||||
export const Switch = (
|
export const Switch = (
|
||||||
props: ComponentPropsWithoutRef<typeof RSwitch.Root>,
|
props: ComponentPropsWithoutRef<typeof RSwitch.Root>,
|
||||||
) => (
|
) => (
|
||||||
<RSwitch.Root className="msk-switch" {...props}>
|
<RSwitch.Root className="modern-sk-switch" {...props}>
|
||||||
<RSwitch.Thumb className="msk-switch__thumb" />
|
<RSwitch.Thumb className="modern-sk-switch__thumb" />
|
||||||
</RSwitch.Root>
|
</RSwitch.Root>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -147,8 +147,8 @@ export const Switch = (
|
|||||||
export const Checkbox = (
|
export const Checkbox = (
|
||||||
props: ComponentPropsWithoutRef<typeof RCheckbox.Root>,
|
props: ComponentPropsWithoutRef<typeof RCheckbox.Root>,
|
||||||
) => (
|
) => (
|
||||||
<RCheckbox.Root className="msk-check" {...props}>
|
<RCheckbox.Root className="modern-sk-check" {...props}>
|
||||||
<RCheckbox.Indicator className="msk-check__indicator">
|
<RCheckbox.Indicator className="modern-sk-check__indicator">
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
@@ -169,8 +169,8 @@ export const RadioItem = ({
|
|||||||
value,
|
value,
|
||||||
...props
|
...props
|
||||||
}: ComponentPropsWithoutRef<typeof RRadioGroup.Item>) => (
|
}: ComponentPropsWithoutRef<typeof RRadioGroup.Item>) => (
|
||||||
<RRadioGroup.Item className="msk-radio" value={value} {...props}>
|
<RRadioGroup.Item className="modern-sk-radio" value={value} {...props}>
|
||||||
<RRadioGroup.Indicator className="msk-radio__indicator" />
|
<RRadioGroup.Indicator className="modern-sk-radio__indicator" />
|
||||||
</RRadioGroup.Item>
|
</RRadioGroup.Item>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ export const Control = ({
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
control: ReactNode;
|
control: ReactNode;
|
||||||
}) => (
|
}) => (
|
||||||
<label className="msk-control">
|
<label className="modern-sk-control">
|
||||||
{control}
|
{control}
|
||||||
{children}
|
{children}
|
||||||
</label>
|
</label>
|
||||||
@@ -201,7 +201,7 @@ export const SegmentedControl = ({
|
|||||||
}: SegProps) => (
|
}: SegProps) => (
|
||||||
<RToggleGroup.Root
|
<RToggleGroup.Root
|
||||||
type="single"
|
type="single"
|
||||||
className="msk-seg"
|
className="modern-sk-seg"
|
||||||
value={value}
|
value={value}
|
||||||
onValueChange={(v) => v && onValueChange(v)}
|
onValueChange={(v) => v && onValueChange(v)}
|
||||||
>
|
>
|
||||||
@@ -209,7 +209,7 @@ export const SegmentedControl = ({
|
|||||||
<RToggleGroup.Item
|
<RToggleGroup.Item
|
||||||
key={it.value}
|
key={it.value}
|
||||||
value={it.value}
|
value={it.value}
|
||||||
className="msk-seg__item"
|
className="modern-sk-seg__item"
|
||||||
>
|
>
|
||||||
{it.label}
|
{it.label}
|
||||||
</RToggleGroup.Item>
|
</RToggleGroup.Item>
|
||||||
@@ -221,11 +221,11 @@ export const SegmentedControl = ({
|
|||||||
export const Slider = (
|
export const Slider = (
|
||||||
props: ComponentPropsWithoutRef<typeof RSlider.Root>,
|
props: ComponentPropsWithoutRef<typeof RSlider.Root>,
|
||||||
) => (
|
) => (
|
||||||
<RSlider.Root className="msk-slider" {...props}>
|
<RSlider.Root className="modern-sk-slider" {...props}>
|
||||||
<RSlider.Track className="msk-slider__track">
|
<RSlider.Track className="modern-sk-slider__track">
|
||||||
<RSlider.Range className="msk-slider__range" />
|
<RSlider.Range className="modern-sk-slider__range" />
|
||||||
</RSlider.Track>
|
</RSlider.Track>
|
||||||
<RSlider.Thumb className="msk-slider__thumb" aria-label="Value" />
|
<RSlider.Thumb className="modern-sk-slider__thumb" aria-label="Value" />
|
||||||
</RSlider.Root>
|
</RSlider.Root>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ export const Stepper = ({
|
|||||||
onDecrement: () => void;
|
onDecrement: () => void;
|
||||||
onIncrement: () => void;
|
onIncrement: () => void;
|
||||||
}) => (
|
}) => (
|
||||||
<div className="msk-stepper">
|
<div className="modern-sk-stepper">
|
||||||
<button type="button" onClick={onDecrement} aria-label="Decrease">
|
<button type="button" onClick={onDecrement} aria-label="Decrease">
|
||||||
−
|
−
|
||||||
</button>
|
</button>
|
||||||
@@ -254,12 +254,12 @@ export const TabsList = ({
|
|||||||
}: {
|
}: {
|
||||||
items: Array<{ value: string; label: string }>;
|
items: Array<{ value: string; label: string }>;
|
||||||
}) => (
|
}) => (
|
||||||
<RTabs.List className="msk-tabs">
|
<RTabs.List className="modern-sk-tabs">
|
||||||
{items.map((it) => (
|
{items.map((it) => (
|
||||||
<RTabs.Trigger
|
<RTabs.Trigger
|
||||||
key={it.value}
|
key={it.value}
|
||||||
value={it.value}
|
value={it.value}
|
||||||
className="msk-tabs__trigger"
|
className="modern-sk-tabs__trigger"
|
||||||
>
|
>
|
||||||
{it.label}
|
{it.label}
|
||||||
</RTabs.Trigger>
|
</RTabs.Trigger>
|
||||||
@@ -270,9 +270,9 @@ export const TabsContent = RTabs.Content;
|
|||||||
|
|
||||||
/* ---------- PROGRESS ---------- */
|
/* ---------- PROGRESS ---------- */
|
||||||
export const Progress = ({ value = 0 }: { value?: number }) => (
|
export const Progress = ({ value = 0 }: { value?: number }) => (
|
||||||
<RProgress.Root className="msk-progress" value={value}>
|
<RProgress.Root className="modern-sk-progress" value={value}>
|
||||||
<RProgress.Indicator
|
<RProgress.Indicator
|
||||||
className="msk-progress__indicator"
|
className="modern-sk-progress__indicator"
|
||||||
style={{ width: `${value}%` }}
|
style={{ width: `${value}%` }}
|
||||||
/>
|
/>
|
||||||
</RProgress.Root>
|
</RProgress.Root>
|
||||||
@@ -292,9 +292,9 @@ export const Badge = ({
|
|||||||
}) => (
|
}) => (
|
||||||
<span
|
<span
|
||||||
className={cx(
|
className={cx(
|
||||||
'msk-badge',
|
'modern-sk-badge',
|
||||||
`msk-badge--${variant}`,
|
`modern-sk-badge--${variant}`,
|
||||||
dot && 'msk-badge--dot',
|
dot && 'modern-sk-badge--dot',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -311,7 +311,7 @@ export const Chip = ({
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
onRemove?: () => void;
|
onRemove?: () => void;
|
||||||
}) => (
|
}) => (
|
||||||
<span className="msk-chip">
|
<span className="modern-sk-chip">
|
||||||
{children}
|
{children}
|
||||||
{onRemove && (
|
{onRemove && (
|
||||||
<button type="button" className="x" onClick={onRemove} aria-label="Remove">
|
<button type="button" className="x" onClick={onRemove} aria-label="Remove">
|
||||||
@@ -326,7 +326,7 @@ export const Card = ({
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: ComponentPropsWithoutRef<'div'>) => (
|
}: ComponentPropsWithoutRef<'div'>) => (
|
||||||
<div className={cx('msk-card', className)} {...props} />
|
<div className={cx('modern-sk-card', className)} {...props} />
|
||||||
);
|
);
|
||||||
|
|
||||||
/* ---------- LIST ---------- */
|
/* ---------- LIST ---------- */
|
||||||
@@ -334,7 +334,7 @@ export const List = ({
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: ComponentPropsWithoutRef<'div'>) => (
|
}: ComponentPropsWithoutRef<'div'>) => (
|
||||||
<div className={cx('msk-list', className)} {...props} />
|
<div className={cx('modern-sk-list', className)} {...props} />
|
||||||
);
|
);
|
||||||
export const Row = ({
|
export const Row = ({
|
||||||
selected,
|
selected,
|
||||||
@@ -342,7 +342,7 @@ export const Row = ({
|
|||||||
...props
|
...props
|
||||||
}: ComponentPropsWithoutRef<'div'> & { selected?: boolean }) => (
|
}: ComponentPropsWithoutRef<'div'> & { selected?: boolean }) => (
|
||||||
<div
|
<div
|
||||||
className={cx('msk-row', selected && 'is-selected', className)}
|
className={cx('modern-sk-row', selected && 'is-selected', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -355,7 +355,7 @@ export const MenuContent = ({
|
|||||||
...props
|
...props
|
||||||
}: ComponentPropsWithoutRef<typeof RMenu.Content>) => (
|
}: ComponentPropsWithoutRef<typeof RMenu.Content>) => (
|
||||||
<RMenu.Portal>
|
<RMenu.Portal>
|
||||||
<RMenu.Content className="msk-menu" sideOffset={6} {...props}>
|
<RMenu.Content className="modern-sk-menu" sideOffset={6} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</RMenu.Content>
|
</RMenu.Content>
|
||||||
</RMenu.Portal>
|
</RMenu.Portal>
|
||||||
@@ -369,19 +369,19 @@ export const MenuItem = ({
|
|||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
shortcut?: string;
|
shortcut?: string;
|
||||||
}) => (
|
}) => (
|
||||||
<RMenu.Item className="msk-menu-item" {...props}>
|
<RMenu.Item className="modern-sk-menu-item" {...props}>
|
||||||
{icon && <span className="ph">{icon}</span>}
|
{icon && <span className="ph">{icon}</span>}
|
||||||
{children}
|
{children}
|
||||||
{shortcut && <span className="sc">{shortcut}</span>}
|
{shortcut && <span className="sc">{shortcut}</span>}
|
||||||
</RMenu.Item>
|
</RMenu.Item>
|
||||||
);
|
);
|
||||||
export const MenuSeparator = () => (
|
export const MenuSeparator = () => (
|
||||||
<RMenu.Separator className="msk-menu-sep" />
|
<RMenu.Separator className="modern-sk-menu-sep" />
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Static menu surface — for showcasing the menu without a trigger. */
|
/* Static menu surface — for showcasing the menu without a trigger. */
|
||||||
export const MenuSurface = ({ children }: { children: ReactNode }) => (
|
export const MenuSurface = ({ children }: { children: ReactNode }) => (
|
||||||
<div className="msk-menu">{children}</div>
|
<div className="modern-sk-menu">{children}</div>
|
||||||
);
|
);
|
||||||
export const MenuRow = ({
|
export const MenuRow = ({
|
||||||
icon,
|
icon,
|
||||||
@@ -392,7 +392,7 @@ export const MenuRow = ({
|
|||||||
shortcut?: string;
|
shortcut?: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) => (
|
}) => (
|
||||||
<div className="msk-menu-item">
|
<div className="modern-sk-menu-item">
|
||||||
{icon && <span className="ph">{icon}</span>}
|
{icon && <span className="ph">{icon}</span>}
|
||||||
{children}
|
{children}
|
||||||
{shortcut && <span className="sc">{shortcut}</span>}
|
{shortcut && <span className="sc">{shortcut}</span>}
|
||||||
@@ -410,7 +410,7 @@ export const Tooltip = ({
|
|||||||
<RTooltip.Root>
|
<RTooltip.Root>
|
||||||
<RTooltip.Trigger asChild>{children}</RTooltip.Trigger>
|
<RTooltip.Trigger asChild>{children}</RTooltip.Trigger>
|
||||||
<RTooltip.Portal>
|
<RTooltip.Portal>
|
||||||
<RTooltip.Content className="msk-tooltip" sideOffset={6}>
|
<RTooltip.Content className="modern-sk-tooltip" sideOffset={6}>
|
||||||
{content}
|
{content}
|
||||||
</RTooltip.Content>
|
</RTooltip.Content>
|
||||||
</RTooltip.Portal>
|
</RTooltip.Portal>
|
||||||
@@ -427,10 +427,10 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
|||||||
<button
|
<button
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cx(
|
className={cx(
|
||||||
'msk-btn',
|
'modern-sk-btn',
|
||||||
'msk-iconbtn',
|
'modern-sk-iconbtn',
|
||||||
variant !== 'key' && `msk-btn--${variant}`,
|
variant !== 'key' && `modern-sk-btn--${variant}`,
|
||||||
size && `msk-iconbtn--${size}`,
|
size && `modern-sk-iconbtn--${size}`,
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -447,12 +447,12 @@ export const Spinner = ({
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: ComponentPropsWithoutRef<'span'> & { size?: 'sm' | 'lg' }) => {
|
}: ComponentPropsWithoutRef<'span'> & { size?: 'sm' | 'lg' }) => {
|
||||||
const gid = `msk-groove-${useId()}`;
|
const gid = `modern-sk-groove-${useId()}`;
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
role="status"
|
role="status"
|
||||||
aria-label="Loading"
|
aria-label="Loading"
|
||||||
className={cx('msk-spinner', size && `msk-spinner--${size}`, className)}
|
className={cx('modern-sk-spinner', size && `modern-sk-spinner--${size}`, className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<svg viewBox="0 0 36 36" fill="none">
|
<svg viewBox="0 0 36 36" fill="none">
|
||||||
@@ -473,7 +473,7 @@ export const Spinner = ({
|
|||||||
<circle cx="18" cy="18" r="14" stroke={`url(#${gid})`} strokeWidth="5" />
|
<circle cx="18" cy="18" r="14" stroke={`url(#${gid})`} strokeWidth="5" />
|
||||||
{/* glossy lime arc, nested inside the groove with rounded ends */}
|
{/* glossy lime arc, nested inside the groove with rounded ends */}
|
||||||
<circle
|
<circle
|
||||||
className="msk-spinner__arc"
|
className="modern-sk-spinner__arc"
|
||||||
cx="18"
|
cx="18"
|
||||||
cy="18"
|
cy="18"
|
||||||
r="14"
|
r="14"
|
||||||
@@ -498,9 +498,9 @@ export const Callout = ({
|
|||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) => (
|
}) => (
|
||||||
<div className={cx('msk-callout', variant !== 'info' && `msk-callout--${variant}`)}>
|
<div className={cx('modern-sk-callout', variant !== 'info' && `modern-sk-callout--${variant}`)}>
|
||||||
{icon && <span className="msk-callout__icon">{icon}</span>}
|
{icon && <span className="modern-sk-callout__icon">{icon}</span>}
|
||||||
<div className="msk-callout__body">{children}</div>
|
<div className="modern-sk-callout__body">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -509,8 +509,8 @@ export const Table = ({
|
|||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: ComponentPropsWithoutRef<'table'>) => (
|
}: ComponentPropsWithoutRef<'table'>) => (
|
||||||
<div className="msk-table-wrap">
|
<div className="modern-sk-table-wrap">
|
||||||
<table className="msk-table" {...props}>
|
<table className="modern-sk-table" {...props}>
|
||||||
{children}
|
{children}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -537,15 +537,15 @@ export const ScrollArea = ({
|
|||||||
className?: string;
|
className?: string;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}) => (
|
}) => (
|
||||||
<RScrollArea.Root className={cx('msk-scroll', className)} style={style}>
|
<RScrollArea.Root className={cx('modern-sk-scroll', className)} style={style}>
|
||||||
<RScrollArea.Viewport className="msk-scroll__viewport">
|
<RScrollArea.Viewport className="modern-sk-scroll__viewport">
|
||||||
{children}
|
{children}
|
||||||
</RScrollArea.Viewport>
|
</RScrollArea.Viewport>
|
||||||
<RScrollArea.Scrollbar className="msk-scroll__bar" orientation="vertical">
|
<RScrollArea.Scrollbar className="modern-sk-scroll__bar" orientation="vertical">
|
||||||
<RScrollArea.Thumb className="msk-scroll__thumb" />
|
<RScrollArea.Thumb className="modern-sk-scroll__thumb" />
|
||||||
</RScrollArea.Scrollbar>
|
</RScrollArea.Scrollbar>
|
||||||
<RScrollArea.Scrollbar className="msk-scroll__bar" orientation="horizontal">
|
<RScrollArea.Scrollbar className="modern-sk-scroll__bar" orientation="horizontal">
|
||||||
<RScrollArea.Thumb className="msk-scroll__thumb" />
|
<RScrollArea.Thumb className="modern-sk-scroll__thumb" />
|
||||||
</RScrollArea.Scrollbar>
|
</RScrollArea.Scrollbar>
|
||||||
<RScrollArea.Corner />
|
<RScrollArea.Corner />
|
||||||
</RScrollArea.Root>
|
</RScrollArea.Root>
|
||||||
@@ -572,21 +572,21 @@ export const Dialog = ({
|
|||||||
<RDialog.Root open={open} onOpenChange={onOpenChange}>
|
<RDialog.Root open={open} onOpenChange={onOpenChange}>
|
||||||
{trigger && <RDialog.Trigger asChild>{trigger}</RDialog.Trigger>}
|
{trigger && <RDialog.Trigger asChild>{trigger}</RDialog.Trigger>}
|
||||||
<RDialog.Portal>
|
<RDialog.Portal>
|
||||||
<RDialog.Overlay className="msk-overlay" />
|
<RDialog.Overlay className="modern-sk-overlay" />
|
||||||
<RDialog.Content className="msk-dialog">
|
<RDialog.Content className="modern-sk-dialog">
|
||||||
<RDialog.Title className="msk-dialog__title">{title}</RDialog.Title>
|
<RDialog.Title className="modern-sk-dialog__title">{title}</RDialog.Title>
|
||||||
{description && (
|
{description && (
|
||||||
<RDialog.Description className="msk-dialog__desc">
|
<RDialog.Description className="modern-sk-dialog__desc">
|
||||||
{description}
|
{description}
|
||||||
</RDialog.Description>
|
</RDialog.Description>
|
||||||
)}
|
)}
|
||||||
{children && <div className="msk-dialog__body">{children}</div>}
|
{children && <div className="modern-sk-dialog__body">{children}</div>}
|
||||||
{footer && <div className="msk-dialog__footer">{footer}</div>}
|
{footer && <div className="modern-sk-dialog__footer">{footer}</div>}
|
||||||
<RDialog.Close asChild>
|
<RDialog.Close asChild>
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="msk-dialog__close"
|
className="modern-sk-dialog__close"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
>
|
>
|
||||||
<X size={14} weight="bold" />
|
<X size={14} weight="bold" />
|
||||||
@@ -624,17 +624,17 @@ export const AlertDialog = ({
|
|||||||
<RAlertDialog.Root open={open} onOpenChange={onOpenChange}>
|
<RAlertDialog.Root open={open} onOpenChange={onOpenChange}>
|
||||||
{trigger && <RAlertDialog.Trigger asChild>{trigger}</RAlertDialog.Trigger>}
|
{trigger && <RAlertDialog.Trigger asChild>{trigger}</RAlertDialog.Trigger>}
|
||||||
<RAlertDialog.Portal>
|
<RAlertDialog.Portal>
|
||||||
<RAlertDialog.Overlay className="msk-overlay" />
|
<RAlertDialog.Overlay className="modern-sk-overlay" />
|
||||||
<RAlertDialog.Content className="msk-dialog">
|
<RAlertDialog.Content className="modern-sk-dialog">
|
||||||
<RAlertDialog.Title className="msk-dialog__title">
|
<RAlertDialog.Title className="modern-sk-dialog__title">
|
||||||
{title}
|
{title}
|
||||||
</RAlertDialog.Title>
|
</RAlertDialog.Title>
|
||||||
{description && (
|
{description && (
|
||||||
<RAlertDialog.Description className="msk-dialog__desc">
|
<RAlertDialog.Description className="modern-sk-dialog__desc">
|
||||||
{description}
|
{description}
|
||||||
</RAlertDialog.Description>
|
</RAlertDialog.Description>
|
||||||
)}
|
)}
|
||||||
<div className="msk-dialog__footer">
|
<div className="modern-sk-dialog__footer">
|
||||||
<RAlertDialog.Cancel asChild>
|
<RAlertDialog.Cancel asChild>
|
||||||
<Button variant="ghost">{cancelLabel}</Button>
|
<Button variant="ghost">{cancelLabel}</Button>
|
||||||
</RAlertDialog.Cancel>
|
</RAlertDialog.Cancel>
|
||||||
@@ -662,11 +662,11 @@ export const Window = ({
|
|||||||
title: string;
|
title: string;
|
||||||
badge?: ReactNode;
|
badge?: ReactNode;
|
||||||
}) => (
|
}) => (
|
||||||
<div className="msk-window" {...props}>
|
<div className="modern-sk-window" {...props}>
|
||||||
<div className="msk-titlebar">
|
<div className="modern-sk-titlebar">
|
||||||
<span className="msk-traffic r" />
|
<span className="modern-sk-traffic r" />
|
||||||
<span className="msk-traffic y" />
|
<span className="modern-sk-traffic y" />
|
||||||
<span className="msk-traffic g" />
|
<span className="modern-sk-traffic g" />
|
||||||
<span className="ttl">{title}</span>
|
<span className="ttl">{title}</span>
|
||||||
{badge && (
|
{badge && (
|
||||||
<div style={{ marginLeft: 'auto', display: 'flex', gap: 8 }}>
|
<div style={{ marginLeft: 'auto', display: 'flex', gap: 8 }}>
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
/* ============================================================
|
/* ============================================================
|
||||||
ModernSK UI — public package entry.
|
ModernSK — public package entry.
|
||||||
Import components from here; import the stylesheet once at your
|
Import components from here; import the stylesheet once at your
|
||||||
app root: import '@modernsk/ui/styles.css';
|
app root: import 'modern-sk/styles.css';
|
||||||
|
Optionally add the branded fonts: import 'modern-sk/fonts.css';
|
||||||
============================================================ */
|
============================================================ */
|
||||||
import { Tooltip } from 'radix-ui';
|
import { Tooltip } from 'radix-ui';
|
||||||
|
|
||||||
|
|||||||
+182
-182
@@ -5,23 +5,23 @@
|
|||||||
([data-state], [data-highlighted], [data-disabled]).
|
([data-state], [data-highlighted], [data-disabled]).
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
@keyframes msk-pop {
|
@keyframes modern-sk-pop {
|
||||||
from { opacity: 0; transform: scale(.6); }
|
from { opacity: 0; transform: scale(.6); }
|
||||||
to { opacity: 1; transform: scale(1); }
|
to { opacity: 1; transform: scale(1); }
|
||||||
}
|
}
|
||||||
@keyframes msk-scale-in {
|
@keyframes modern-sk-scale-in {
|
||||||
from { opacity: 0; transform: scale(.96); }
|
from { opacity: 0; transform: scale(.96); }
|
||||||
to { opacity: 1; transform: scale(1); }
|
to { opacity: 1; transform: scale(1); }
|
||||||
}
|
}
|
||||||
@keyframes msk-scale-out {
|
@keyframes modern-sk-scale-out {
|
||||||
from { opacity: 1; transform: scale(1); }
|
from { opacity: 1; transform: scale(1); }
|
||||||
to { opacity: 0; transform: scale(.96); }
|
to { opacity: 0; transform: scale(.96); }
|
||||||
}
|
}
|
||||||
@keyframes msk-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
@keyframes modern-sk-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
||||||
@keyframes msk-fade-out { from { opacity: 1; } to { opacity: 0; } }
|
@keyframes modern-sk-fade-out { from { opacity: 1; } to { opacity: 0; } }
|
||||||
|
|
||||||
/* ---------- BUTTONS ---------- */
|
/* ---------- BUTTONS ---------- */
|
||||||
.msk-btn{
|
.modern-sk-btn{
|
||||||
font-family:var(--font-sans); font-size:var(--ctl-font); font-weight:600;
|
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);
|
line-height:1.1; padding:var(--ctl-pad-y) var(--ctl-pad-x);
|
||||||
border-radius:var(--r-md); cursor:pointer; user-select:none;
|
border-radius:var(--r-md); cursor:pointer; user-select:none;
|
||||||
@@ -34,60 +34,60 @@
|
|||||||
transform var(--dur-press) var(--ease-out),
|
transform var(--dur-press) var(--ease-out),
|
||||||
background var(--dur-quick) var(--ease-out);
|
background var(--dur-quick) var(--ease-out);
|
||||||
}
|
}
|
||||||
.msk-btn:hover{ background:var(--grad-key-hover); }
|
.modern-sk-btn:hover{ background:var(--grad-key-hover); }
|
||||||
.msk-btn:active{ background:var(--grad-key-down); box-shadow:var(--shadow-pressed); transform:translateY(1px); }
|
.modern-sk-btn:active{ background:var(--grad-key-down); box-shadow:var(--shadow-pressed); transform:translateY(1px); }
|
||||||
.msk-btn:focus-visible{ outline:none; box-shadow:var(--focus-ring), var(--shadow-raised); }
|
.modern-sk-btn:focus-visible{ outline:none; box-shadow:var(--focus-ring), var(--shadow-raised); }
|
||||||
.msk-btn[disabled], .msk-btn.is-disabled{ opacity:.4; filter:saturate(.6); pointer-events:none; }
|
.modern-sk-btn[disabled], .modern-sk-btn.is-disabled{ opacity:.4; filter:saturate(.6); pointer-events:none; }
|
||||||
.msk-btn .ph{ font-size:1.05em; display:inline-flex; }
|
.modern-sk-btn .ph{ font-size:1.05em; display:inline-flex; }
|
||||||
|
|
||||||
.msk-btn--primary{
|
.modern-sk-btn--primary{
|
||||||
color:var(--lime-ink); background:var(--grad-primary);
|
color:var(--lime-ink); background:var(--grad-primary);
|
||||||
border-color:var(--lime-deep);
|
border-color:var(--lime-deep);
|
||||||
text-shadow:0 1px 0 rgba(255,255,255,.25);
|
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);
|
box-shadow:0 1px 0 rgba(255,255,255,.3) inset, var(--glow-lime), 0 2px 4px rgba(0,0,0,.35);
|
||||||
}
|
}
|
||||||
.msk-btn--primary:hover{ filter:brightness(1.04); background:var(--grad-primary); }
|
.modern-sk-btn--primary:hover{ filter:brightness(1.04); background:var(--grad-primary); }
|
||||||
.msk-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:active{ background:var(--grad-primary-down); box-shadow:0 3px 6px rgba(60,90,10,.5) inset; transform:translateY(1px); }
|
||||||
.msk-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--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); }
|
||||||
|
|
||||||
.msk-btn--ember{
|
.modern-sk-btn--ember{
|
||||||
color:#fff; background:var(--grad-ember);
|
color:#fff; background:var(--grad-ember);
|
||||||
border-color:var(--ember-deep);
|
border-color:var(--ember-deep);
|
||||||
text-shadow:0 1px 1px rgba(0,0,0,.25);
|
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);
|
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);
|
||||||
}
|
}
|
||||||
.msk-btn--ember:hover{ background:var(--grad-ember); filter:brightness(1.06); }
|
.modern-sk-btn--ember:hover{ background:var(--grad-ember); filter:brightness(1.06); }
|
||||||
.msk-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:active{ background:var(--grad-ember-down); box-shadow:0 3px 6px rgba(90,30,10,.55) inset; transform:translateY(1px); }
|
||||||
.msk-btn--ember:focus-visible{ box-shadow:var(--focus-ring-ember), 0 2px 4px rgba(0,0,0,.35); }
|
.modern-sk-btn--ember:focus-visible{ box-shadow:var(--focus-ring-ember), 0 2px 4px rgba(0,0,0,.35); }
|
||||||
|
|
||||||
.msk-btn--ghost{
|
.modern-sk-btn--ghost{
|
||||||
color:var(--fg-2); background:transparent; border-color:var(--hair-strong);
|
color:var(--fg-2); background:transparent; border-color:var(--hair-strong);
|
||||||
box-shadow:none; text-shadow:none;
|
box-shadow:none; text-shadow:none;
|
||||||
}
|
}
|
||||||
.msk-btn--ghost:hover{ background:rgba(255,255,255,.04); color:var(--fg-1); }
|
.modern-sk-btn--ghost:hover{ background:rgba(255,255,255,.04); color:var(--fg-1); }
|
||||||
.msk-btn--ghost:active{ background:rgba(0,0,0,.2); box-shadow:var(--shadow-pressed); transform:translateY(1px); }
|
.modern-sk-btn--ghost:active{ background:rgba(0,0,0,.2); box-shadow:var(--shadow-pressed); transform:translateY(1px); }
|
||||||
|
|
||||||
.msk-btn--sm{ font-size:12px; padding:4px 11px; gap:5px; }
|
.modern-sk-btn--sm{ font-size:12px; padding:4px 11px; gap:5px; }
|
||||||
.msk-btn--icon{ padding:var(--ctl-pad-y); width:calc(var(--ctl-font) + 2*var(--ctl-pad-y)); aspect-ratio:1; }
|
.modern-sk-btn--icon{ padding:var(--ctl-pad-y); width:calc(var(--ctl-font) + 2*var(--ctl-pad-y)); aspect-ratio:1; }
|
||||||
|
|
||||||
/* ---------- TEXT FIELDS ---------- */
|
/* ---------- TEXT FIELDS ---------- */
|
||||||
.msk-field{
|
.modern-sk-field{
|
||||||
width:100%; font-family:var(--font-sans); font-size:14px; color:var(--fg-1);
|
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);
|
padding:var(--field-pad-y) var(--field-pad-x); border-radius:var(--r-md);
|
||||||
background:var(--steel-900); border:1px solid var(--edge-inset);
|
background:var(--steel-900); border:1px solid var(--edge-inset);
|
||||||
box-shadow:var(--shadow-inset-well); outline:none;
|
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);
|
transition:box-shadow var(--dur-quick) var(--ease-out), border-color var(--dur-quick) var(--ease-out);
|
||||||
}
|
}
|
||||||
.msk-field::placeholder{ color:var(--fg-3); }
|
.modern-sk-field::placeholder{ color:var(--fg-3); }
|
||||||
.msk-field:focus{ border-color:var(--lime-deep); box-shadow:var(--shadow-inset-well), var(--focus-ring); }
|
.modern-sk-field:focus{ border-color:var(--lime-deep); box-shadow:var(--shadow-inset-well), var(--focus-ring); }
|
||||||
textarea.msk-field{ resize:vertical; min-height:64px; line-height:1.5; }
|
textarea.modern-sk-field{ resize:vertical; min-height:64px; line-height:1.5; }
|
||||||
|
|
||||||
.msk-search{ position:relative; display:flex; align-items:center; }
|
.modern-sk-search{ position:relative; display:flex; align-items:center; }
|
||||||
.msk-search .ph{ position:absolute; left:11px; color:var(--fg-3); font-size:16px; pointer-events:none; display:inline-flex; }
|
.modern-sk-search .ph{ position:absolute; left:11px; color:var(--fg-3); font-size:16px; pointer-events:none; display:inline-flex; }
|
||||||
.msk-search .msk-field{ padding-left:34px; }
|
.modern-sk-search .modern-sk-field{ padding-left:34px; }
|
||||||
|
|
||||||
/* ---------- SELECT (Radix Select, styled as the glossy key) ---------- */
|
/* ---------- SELECT (Radix Select, styled as the glossy key) ---------- */
|
||||||
.msk-select{
|
.modern-sk-select{
|
||||||
font-family:var(--font-sans); font-size:14px; color:var(--fg-1); cursor:pointer;
|
font-family:var(--font-sans); font-size:14px; color:var(--fg-1); cursor:pointer;
|
||||||
display:inline-flex; align-items:center; gap:10px;
|
display:inline-flex; align-items:center; gap:10px;
|
||||||
padding:var(--field-pad-y) 12px var(--field-pad-y) var(--field-pad-x);
|
padding:var(--field-pad-y) 12px var(--field-pad-y) var(--field-pad-x);
|
||||||
@@ -95,257 +95,257 @@ textarea.msk-field{ resize:vertical; min-height:64px; line-height:1.5; }
|
|||||||
background:var(--grad-key);
|
background:var(--grad-key);
|
||||||
box-shadow:var(--shadow-raised); outline:none;
|
box-shadow:var(--shadow-raised); outline:none;
|
||||||
}
|
}
|
||||||
.msk-select:hover{ background:var(--grad-key-hover); }
|
.modern-sk-select:hover{ background:var(--grad-key-hover); }
|
||||||
.msk-select:focus-visible{ box-shadow:var(--focus-ring), var(--shadow-raised); }
|
.modern-sk-select:focus-visible{ box-shadow:var(--focus-ring), var(--shadow-raised); }
|
||||||
.msk-select__icon{ color:var(--fg-2); display:inline-flex; margin-left:auto; }
|
.modern-sk-select__icon{ color:var(--fg-2); display:inline-flex; margin-left:auto; }
|
||||||
.msk-select__content{
|
.modern-sk-select__content{
|
||||||
min-width:var(--radix-select-trigger-width); padding:6px; border-radius:var(--r-lg);
|
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);
|
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);
|
backdrop-filter:blur(24px) saturate(1.3); -webkit-backdrop-filter:blur(24px) saturate(1.3);
|
||||||
box-shadow:var(--shadow-window); z-index:50;
|
box-shadow:var(--shadow-window); z-index:50;
|
||||||
transform-origin:var(--radix-select-content-transform-origin);
|
transform-origin:var(--radix-select-content-transform-origin);
|
||||||
}
|
}
|
||||||
.msk-select__content[data-state="open"]{ animation:msk-scale-in var(--dur-base) var(--ease-out); }
|
.modern-sk-select__content[data-state="open"]{ animation:modern-sk-scale-in var(--dur-base) var(--ease-out); }
|
||||||
.msk-select__item{
|
.modern-sk-select__item{
|
||||||
display:flex; align-items:center; gap:10px; padding:7px 10px; border-radius:var(--r-sm);
|
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;
|
font-size:13px; color:var(--fg-1); cursor:pointer; outline:none; user-select:none;
|
||||||
}
|
}
|
||||||
.msk-select__item[data-highlighted]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); color:var(--lime-ink); }
|
.modern-sk-select__item[data-highlighted]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); color:var(--lime-ink); }
|
||||||
.msk-select__item-indicator{ margin-left:auto; display:inline-flex; }
|
.modern-sk-select__item-indicator{ margin-left:auto; display:inline-flex; }
|
||||||
|
|
||||||
/* ---------- SWITCH ---------- */
|
/* ---------- SWITCH ---------- */
|
||||||
.msk-switch{
|
.modern-sk-switch{
|
||||||
position:relative; width:var(--switch-w); height:var(--switch-h);
|
position:relative; width:var(--switch-w); height:var(--switch-h);
|
||||||
flex-shrink:0; border-radius:8px; padding:0; border:1px solid var(--edge-inset);
|
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;
|
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);
|
transition:background var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
|
||||||
}
|
}
|
||||||
.msk-switch__thumb{
|
.modern-sk-switch__thumb{
|
||||||
display:block; position:absolute; top:50%; left:var(--switch-gap); transform:translateY(-50%);
|
display:block; position:absolute; top:50%; left:var(--switch-gap); transform:translateY(-50%);
|
||||||
width:var(--switch-knob); height:var(--switch-knob); border-radius:5px;
|
width:var(--switch-knob); height:var(--switch-knob); border-radius:5px;
|
||||||
background:linear-gradient(180deg,#cfd1c4,#a7a99c);
|
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;
|
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);
|
transition:left var(--dur-base) var(--ease-snap), background var(--dur-base) var(--ease-out);
|
||||||
}
|
}
|
||||||
.msk-switch[data-state="checked"]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); border-color:var(--lime-deep); }
|
.modern-sk-switch[data-state="checked"]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); border-color:var(--lime-deep); }
|
||||||
.msk-switch[data-state="checked"] .msk-switch__thumb{ left:calc(100% - var(--switch-knob) - var(--switch-gap)); background:linear-gradient(180deg,#fff,#e6e8dd); }
|
.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); }
|
||||||
.msk-switch:focus-visible{ outline:none; box-shadow:var(--shadow-inset-well), var(--focus-ring); }
|
.modern-sk-switch:focus-visible{ outline:none; box-shadow:var(--shadow-inset-well), var(--focus-ring); }
|
||||||
|
|
||||||
/* ---------- CHECKBOX ---------- */
|
/* ---------- CHECKBOX ---------- */
|
||||||
.msk-check{
|
.modern-sk-check{
|
||||||
width:22px; height:22px; flex-shrink:0; border-radius:4px; padding:0;
|
width:22px; height:22px; flex-shrink:0; border-radius:4px; padding:0;
|
||||||
background:var(--steel-900); border:1px solid var(--edge-inset);
|
background:var(--steel-900); border:1px solid var(--edge-inset);
|
||||||
box-shadow:var(--shadow-inset-well); cursor:pointer;
|
box-shadow:var(--shadow-inset-well); cursor:pointer;
|
||||||
display:flex; align-items:center; justify-content:center;
|
display:flex; align-items:center; justify-content:center;
|
||||||
transition:background var(--dur-quick) var(--ease-out), border-color var(--dur-quick) var(--ease-out);
|
transition:background var(--dur-quick) var(--ease-out), border-color var(--dur-quick) var(--ease-out);
|
||||||
}
|
}
|
||||||
.msk-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[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; }
|
||||||
.msk-check__indicator{ display:flex; align-items:center; justify-content:center; animation:msk-pop var(--dur-quick) var(--ease-snap); }
|
.modern-sk-check__indicator{ display:flex; align-items:center; justify-content:center; animation:modern-sk-pop var(--dur-quick) var(--ease-snap); }
|
||||||
.msk-check__indicator svg{ width:13px; height:13px; display:block; }
|
.modern-sk-check__indicator svg{ width:13px; height:13px; display:block; }
|
||||||
.msk-check:focus-visible{ outline:none; box-shadow:var(--shadow-inset-well), var(--focus-ring); }
|
.modern-sk-check:focus-visible{ outline:none; box-shadow:var(--shadow-inset-well), var(--focus-ring); }
|
||||||
|
|
||||||
/* ---------- RADIO ---------- */
|
/* ---------- RADIO ---------- */
|
||||||
.msk-radio{
|
.modern-sk-radio{
|
||||||
width:22px; height:22px; flex-shrink:0; border-radius:50%; padding:0;
|
width:22px; height:22px; flex-shrink:0; border-radius:50%; padding:0;
|
||||||
background:var(--steel-900); border:1px solid var(--edge-inset);
|
background:var(--steel-900); border:1px solid var(--edge-inset);
|
||||||
box-shadow:var(--shadow-inset-well); cursor:pointer;
|
box-shadow:var(--shadow-inset-well); cursor:pointer;
|
||||||
display:flex; align-items:center; justify-content:center;
|
display:flex; align-items:center; justify-content:center;
|
||||||
transition:border-color var(--dur-quick) var(--ease-out);
|
transition:border-color var(--dur-quick) var(--ease-out);
|
||||||
}
|
}
|
||||||
.msk-radio__indicator{ display:flex; align-items:center; justify-content:center; width:100%; height:100%; }
|
.modern-sk-radio__indicator{ display:flex; align-items:center; justify-content:center; width:100%; height:100%; }
|
||||||
.msk-radio__indicator::after{
|
.modern-sk-radio__indicator::after{
|
||||||
content:""; width:11px; height:11px; border-radius:50%;
|
content:""; width:11px; height:11px; border-radius:50%;
|
||||||
background:radial-gradient(circle at 50% 38%,var(--lime-bright),var(--lime-deep));
|
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;
|
box-shadow:0 0 8px rgba(190,242,100,.45), 0 1px 0 rgba(255,255,255,.45) inset;
|
||||||
animation:msk-pop var(--dur-quick) var(--ease-snap);
|
animation:modern-sk-pop var(--dur-quick) var(--ease-snap);
|
||||||
}
|
}
|
||||||
.msk-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 */
|
/* control + label row helper */
|
||||||
.msk-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) ---------- */
|
/* ---------- SEGMENTED CONTROL (Radix ToggleGroup) ---------- */
|
||||||
.msk-seg{ 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{ 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; }
|
||||||
.msk-seg__item{ 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), background var(--dur-quick); }
|
.modern-sk-seg__item{ 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), background var(--dur-quick); }
|
||||||
.msk-seg__item:hover{ color:var(--fg-1); }
|
.modern-sk-seg__item:hover{ color:var(--fg-1); }
|
||||||
.msk-seg__item[data-state="on"]{ color:var(--fg-1); background:var(--grad-key); box-shadow:var(--shadow-raised); }
|
.modern-sk-seg__item[data-state="on"]{ color:var(--fg-1); background:var(--grad-key); box-shadow:var(--shadow-raised); }
|
||||||
|
|
||||||
/* ---------- SLIDER ---------- */
|
/* ---------- SLIDER ---------- */
|
||||||
.msk-slider{ position:relative; display:flex; align-items:center; width:200px; height:20px; user-select:none; touch-action:none; }
|
.modern-sk-slider{ position:relative; display:flex; align-items:center; width:200px; height:20px; user-select:none; touch-action:none; }
|
||||||
.msk-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__track{ position:relative; flex-grow:1; height:6px; border-radius:3px; background:var(--steel-800); box-shadow:var(--shadow-inset-well); }
|
||||||
.msk-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__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); }
|
||||||
.msk-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{ 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; }
|
||||||
.msk-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__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); }
|
||||||
|
|
||||||
/* ---------- STEPPER ---------- */
|
/* ---------- STEPPER ---------- */
|
||||||
.msk-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{ display:inline-flex; border-radius:var(--r-md); overflow:hidden; box-shadow:var(--shadow-raised); border:1px solid var(--hair-strong); }
|
||||||
.msk-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{ 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); }
|
||||||
.msk-stepper button:hover{ background:var(--grad-key-hover); }
|
.modern-sk-stepper button:hover{ background:var(--grad-key-hover); }
|
||||||
.msk-stepper button:active{ background:var(--grad-key-down); }
|
.modern-sk-stepper button:active{ background:var(--grad-key-down); }
|
||||||
.msk-stepper button:first-child{ border-right:1px solid var(--edge-inset); }
|
.modern-sk-stepper button:first-child{ border-right:1px solid var(--edge-inset); }
|
||||||
|
|
||||||
/* ---------- BADGES / CHIPS / TAGS ---------- */
|
/* ---------- BADGES / CHIPS / TAGS ---------- */
|
||||||
.msk-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{ 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; }
|
||||||
.msk-badge--lime{ color:var(--lime-ink); background:linear-gradient(180deg,var(--lime-bright),var(--lime-deep)); border-color:var(--lime-deep); }
|
.modern-sk-badge--lime{ color:var(--lime-ink); background:linear-gradient(180deg,var(--lime-bright),var(--lime-deep)); border-color:var(--lime-deep); }
|
||||||
.msk-badge--ember{ color:#fff; background:var(--grad-ember); border-color:var(--ember-deep); }
|
.modern-sk-badge--ember{ color:#fff; background:var(--grad-ember); border-color:var(--ember-deep); }
|
||||||
.msk-badge--neutral{ color:var(--fg-2); background:var(--steel-700); border-color:var(--hair-strong); }
|
.modern-sk-badge--neutral{ color:var(--fg-2); background:var(--steel-700); border-color:var(--hair-strong); }
|
||||||
.msk-badge--outline{ color:var(--fg-2); background:transparent; border-color:var(--hair-strong); }
|
.modern-sk-badge--outline{ color:var(--fg-2); background:transparent; border-color:var(--hair-strong); }
|
||||||
.msk-badge--dot::before{ content:""; width:6px; height:6px; border-radius:50%; background:currentColor; }
|
.modern-sk-badge--dot::before{ content:""; width:6px; height:6px; border-radius:50%; background:currentColor; }
|
||||||
|
|
||||||
.msk-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{ 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); }
|
||||||
.msk-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{ color:var(--fg-3); cursor:pointer; font-size:14px; line-height:1; background:none; border:none; padding:0; display:inline-flex; }
|
||||||
.msk-chip .x:hover{ color:var(--fg-1); }
|
.modern-sk-chip .x:hover{ color:var(--fg-1); }
|
||||||
|
|
||||||
/* ---------- CARD ---------- */
|
/* ---------- CARD ---------- */
|
||||||
.msk-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 ---------- */
|
/* ---------- LIST ROWS ---------- */
|
||||||
.msk-list{ background:var(--steel-900); border:1px solid var(--hair); border-radius:var(--r-lg); box-shadow:var(--shadow-card); overflow:hidden; }
|
.modern-sk-list{ background:var(--steel-900); border:1px solid var(--hair); border-radius:var(--r-lg); box-shadow:var(--shadow-card); overflow:hidden; }
|
||||||
.msk-row{ display:flex; align-items:center; gap:12px; padding:10px 14px; border-bottom:1px solid var(--hair); cursor:default; }
|
.modern-sk-row{ display:flex; align-items:center; gap:12px; padding:10px 14px; border-bottom:1px solid var(--hair); cursor:default; }
|
||||||
.msk-row:last-child{ border-bottom:none; }
|
.modern-sk-row:last-child{ border-bottom:none; }
|
||||||
.msk-row:hover{ background:rgba(255,255,255,.03); }
|
.modern-sk-row:hover{ background:rgba(255,255,255,.03); }
|
||||||
.msk-row.is-selected{ background:linear-gradient(180deg,rgba(190,242,100,.16),rgba(190,242,100,.08)); }
|
.modern-sk-row.is-selected{ background:linear-gradient(180deg,rgba(190,242,100,.16),rgba(190,242,100,.08)); }
|
||||||
.msk-row .nm{ font-size:14px; font-weight:500; color:var(--fg-1); white-space:nowrap; }
|
.modern-sk-row .nm{ font-size:14px; font-weight:500; color:var(--fg-1); white-space:nowrap; }
|
||||||
.msk-row .meta{ margin-left:auto; font-family:var(--font-mono); font-size:11px; color:var(--fg-3); }
|
.modern-sk-row .meta{ margin-left:auto; font-family:var(--font-mono); font-size:11px; color:var(--fg-3); }
|
||||||
|
|
||||||
/* ---------- MENU / POPOVER (Radix DropdownMenu) ---------- */
|
/* ---------- MENU / POPOVER (Radix DropdownMenu) ---------- */
|
||||||
.msk-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{ 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); }
|
||||||
.msk-menu[data-state="open"]{ animation:msk-scale-in var(--dur-base) var(--ease-out); }
|
.modern-sk-menu[data-state="open"]{ animation:modern-sk-scale-in var(--dur-base) var(--ease-out); }
|
||||||
.msk-menu[data-state="closed"]{ animation:msk-scale-out var(--dur-quick) var(--ease-out); }
|
.modern-sk-menu[data-state="closed"]{ animation:modern-sk-scale-out var(--dur-quick) var(--ease-out); }
|
||||||
.msk-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{ 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; }
|
||||||
.msk-menu-item .ph{ font-size:16px; color:var(--fg-2); display:inline-flex; }
|
.modern-sk-menu-item .ph{ font-size:16px; color:var(--fg-2); display:inline-flex; }
|
||||||
.msk-menu-item .sc{ margin-left:auto; font-family:var(--font-mono); font-size:11px; color:var(--fg-3); }
|
.modern-sk-menu-item .sc{ margin-left:auto; font-family:var(--font-mono); font-size:11px; color:var(--fg-3); }
|
||||||
.msk-menu-item[data-highlighted]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); color:var(--lime-ink); }
|
.modern-sk-menu-item[data-highlighted]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); color:var(--lime-ink); }
|
||||||
.msk-menu-item[data-highlighted] .ph, .msk-menu-item[data-highlighted] .sc{ color:var(--lime-ink); }
|
.modern-sk-menu-item[data-highlighted] .ph, .modern-sk-menu-item[data-highlighted] .sc{ color:var(--lime-ink); }
|
||||||
.msk-menu-sep{ height:1px; background:var(--hair); margin:5px 4px; }
|
.modern-sk-menu-sep{ height:1px; background:var(--hair); margin:5px 4px; }
|
||||||
|
|
||||||
/* ---------- TABS (Radix Tabs) ---------- */
|
/* ---------- TABS (Radix Tabs) ---------- */
|
||||||
.msk-tabs{ display:flex; gap:2px; border-bottom:1px solid var(--hair); }
|
.modern-sk-tabs{ display:flex; gap:2px; border-bottom:1px solid var(--hair); }
|
||||||
.msk-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{ 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); }
|
||||||
.msk-tabs__trigger:hover{ color:var(--fg-2); }
|
.modern-sk-tabs__trigger:hover{ color:var(--fg-2); }
|
||||||
.msk-tabs__trigger[data-state="active"]{ color:var(--fg-1); }
|
.modern-sk-tabs__trigger[data-state="active"]{ color:var(--fg-1); }
|
||||||
.msk-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__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); }
|
||||||
|
|
||||||
/* ---------- PROGRESS (Radix Progress) ---------- */
|
/* ---------- PROGRESS (Radix Progress) ---------- */
|
||||||
.msk-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{ width:100%; height:8px; border-radius:var(--r-pill); background:var(--steel-800); box-shadow:var(--shadow-inset-well); overflow:hidden; }
|
||||||
.msk-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__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); }
|
||||||
|
|
||||||
/* ---------- WINDOW CHROME ---------- */
|
/* ---------- WINDOW CHROME ---------- */
|
||||||
.msk-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-window{ border-radius:var(--r-xl); overflow:hidden; border:1px solid var(--hair-strong); background:var(--steel-800); box-shadow:var(--shadow-window); }
|
||||||
.msk-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{ 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; }
|
||||||
.msk-titlebar .ttl{ margin-left:6px; font-size:13px; font-weight:600; color:var(--fg-2); }
|
.modern-sk-titlebar .ttl{ margin-left:6px; font-size:13px; font-weight:600; color:var(--fg-2); }
|
||||||
.msk-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{ 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); }
|
||||||
.msk-traffic.r{ background:radial-gradient(circle at 35% 30%,#ff8a7a,#ec5f55); }
|
.modern-sk-traffic.r{ background:radial-gradient(circle at 35% 30%,#ff8a7a,#ec5f55); }
|
||||||
.msk-traffic.y{ background:radial-gradient(circle at 35% 30%,#ffd97a,#e6a93c); }
|
.modern-sk-traffic.y{ background:radial-gradient(circle at 35% 30%,#ffd97a,#e6a93c); }
|
||||||
.msk-traffic.g{ background:radial-gradient(circle at 35% 30%,#bff07a,#9bce4c); }
|
.modern-sk-traffic.g{ background:radial-gradient(circle at 35% 30%,#bff07a,#9bce4c); }
|
||||||
|
|
||||||
/* ---------- TOOLTIP (Radix Tooltip) ---------- */
|
/* ---------- TOOLTIP (Radix Tooltip) ---------- */
|
||||||
.msk-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{ 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); }
|
||||||
.msk-tooltip[data-state="delayed-open"], .msk-tooltip[data-state="instant-open"]{ animation:msk-scale-in var(--dur-base) var(--ease-out); }
|
.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); }
|
||||||
.msk-tooltip[data-state="closed"]{ animation:msk-fade-out var(--dur-quick) var(--ease-out); }
|
.modern-sk-tooltip[data-state="closed"]{ animation:modern-sk-fade-out var(--dur-quick) var(--ease-out); }
|
||||||
|
|
||||||
/* ---------- ICON BUTTON ----------
|
/* ---------- ICON BUTTON ----------
|
||||||
Square tactile key. Reuses the .msk-btn engine; variants below
|
Square tactile key. Reuses the .modern-sk-btn engine; variants below
|
||||||
just recolor. Pair with .msk-btn / .msk-btn--primary etc. */
|
just recolor. Pair with .modern-sk-btn / .modern-sk-btn--primary etc. */
|
||||||
.msk-iconbtn{ padding:0; width:32px; height:32px; aspect-ratio:1; display:inline-flex; align-items:center; justify-content:center; }
|
.modern-sk-iconbtn{ padding:0; width:32px; height:32px; aspect-ratio:1; display:inline-flex; align-items:center; justify-content:center; }
|
||||||
.msk-iconbtn--sm{ width:26px; height:26px; }
|
.modern-sk-iconbtn--sm{ width:26px; height:26px; }
|
||||||
.msk-iconbtn--lg{ width:38px; height:38px; }
|
.modern-sk-iconbtn--lg{ width:38px; height:38px; }
|
||||||
|
|
||||||
/* ---------- SPINNER ----------
|
/* ---------- SPINNER ----------
|
||||||
A carved donut groove (sunk like the switch well) with a glossy lime
|
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
|
arc spinning *inside* the channel. All drawn in SVG; CSS only sizes
|
||||||
the box and drives the rotation. Honors reduced motion. */
|
the box and drives the rotation. Honors reduced motion. */
|
||||||
.msk-spinner{ display:inline-block; width:24px; height:24px; line-height:0; }
|
.modern-sk-spinner{ display:inline-block; width:24px; height:24px; line-height:0; }
|
||||||
.msk-spinner--sm{ width:16px; height:16px; }
|
.modern-sk-spinner--sm{ width:16px; height:16px; }
|
||||||
.msk-spinner--lg{ width:34px; height:34px; }
|
.modern-sk-spinner--lg{ width:34px; height:34px; }
|
||||||
.msk-spinner svg{ width:100%; height:100%; display:block; }
|
.modern-sk-spinner svg{ width:100%; height:100%; display:block; }
|
||||||
.msk-spinner__arc{
|
.modern-sk-spinner__arc{
|
||||||
transform-origin:center; animation:msk-spin .7s linear infinite;
|
transform-origin:center; animation:modern-sk-spin .7s linear infinite;
|
||||||
filter:drop-shadow(0 0 3px rgba(190,242,100,.55));
|
filter:drop-shadow(0 0 3px rgba(190,242,100,.55));
|
||||||
}
|
}
|
||||||
@keyframes msk-spin{ to{ transform:rotate(360deg); } }
|
@keyframes modern-sk-spin{ to{ transform:rotate(360deg); } }
|
||||||
@media (prefers-reduced-motion:reduce){ .msk-spinner__arc{ animation-duration:1.8s; } }
|
@media (prefers-reduced-motion:reduce){ .modern-sk-spinner__arc{ animation-duration:1.8s; } }
|
||||||
|
|
||||||
/* ---------- CALLOUT ----------
|
/* ---------- CALLOUT ----------
|
||||||
Soft semantic surface (12–14% tint on the felt) with a glossy
|
Soft semantic surface (12–14% tint on the felt) with a glossy
|
||||||
icon chip. Variants reuse the semantic --*-bg tokens. */
|
icon chip. Variants reuse the semantic --*-bg tokens. */
|
||||||
.msk-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{ 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); }
|
||||||
.msk-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__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); }
|
||||||
.msk-callout__body{ font-size:13px; line-height:1.5; color:var(--fg-2); padding-top:3px; }
|
.modern-sk-callout__body{ font-size:13px; line-height:1.5; color:var(--fg-2); padding-top:3px; }
|
||||||
.msk-callout__body strong{ color:var(--fg-1); font-weight:600; }
|
.modern-sk-callout__body strong{ color:var(--fg-1); font-weight:600; }
|
||||||
.msk-callout--success{ background:var(--success-bg); border-color:rgba(190,242,100,.22); }
|
.modern-sk-callout--success{ background:var(--success-bg); border-color:rgba(190,242,100,.22); }
|
||||||
.msk-callout--success .msk-callout__icon{ color:var(--success); }
|
.modern-sk-callout--success .modern-sk-callout__icon{ color:var(--success); }
|
||||||
.msk-callout--warning{ background:var(--warning-bg); border-color:rgba(230,169,60,.22); }
|
.modern-sk-callout--warning{ background:var(--warning-bg); border-color:rgba(230,169,60,.22); }
|
||||||
.msk-callout--warning .msk-callout__icon{ color:var(--warning); }
|
.modern-sk-callout--warning .modern-sk-callout__icon{ color:var(--warning); }
|
||||||
.msk-callout--danger{ background:var(--danger-bg); border-color:rgba(233,87,43,.25); }
|
.modern-sk-callout--danger{ background:var(--danger-bg); border-color:rgba(233,87,43,.25); }
|
||||||
.msk-callout--danger .msk-callout__icon{ color:var(--danger); }
|
.modern-sk-callout--danger .modern-sk-callout__icon{ color:var(--danger); }
|
||||||
|
|
||||||
/* ---------- TABLE ----------
|
/* ---------- TABLE ----------
|
||||||
The list-row aesthetic stretched to columns: hairline grid, sunk
|
The list-row aesthetic stretched to columns: hairline grid, sunk
|
||||||
header strip, mono numerals, lime row selection. */
|
header strip, mono numerals, lime row selection. */
|
||||||
.msk-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-wrap{ background:var(--steel-900); border:1px solid var(--hair); border-radius:var(--r-lg); box-shadow:var(--shadow-card); overflow:hidden; }
|
||||||
.msk-table{ width:100%; border-collapse:collapse; font-size:14px; }
|
.modern-sk-table{ width:100%; border-collapse:collapse; font-size:14px; }
|
||||||
.msk-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 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; }
|
||||||
.msk-table tbody td{ padding:10px 14px; border-bottom:1px solid var(--hair); color:var(--fg-1); vertical-align:middle; }
|
.modern-sk-table tbody td{ padding:10px 14px; border-bottom:1px solid var(--hair); color:var(--fg-1); vertical-align:middle; }
|
||||||
.msk-table tbody tr:last-child td{ border-bottom:none; }
|
.modern-sk-table tbody tr:last-child td{ border-bottom:none; }
|
||||||
.msk-table tbody tr:hover td{ background:rgba(255,255,255,.03); }
|
.modern-sk-table tbody tr:hover td{ background:rgba(255,255,255,.03); }
|
||||||
.msk-table tbody tr.is-selected td{ background:linear-gradient(180deg,rgba(190,242,100,.16),rgba(190,242,100,.08)); }
|
.modern-sk-table tbody tr.is-selected td{ background:linear-gradient(180deg,rgba(190,242,100,.16),rgba(190,242,100,.08)); }
|
||||||
.msk-table .num{ font-family:var(--font-mono); font-size:12px; color:var(--fg-2); text-align:right; }
|
.modern-sk-table .num{ font-family:var(--font-mono); font-size:12px; color:var(--fg-2); text-align:right; }
|
||||||
.msk-table .muted{ color:var(--fg-3); }
|
.modern-sk-table .muted{ color:var(--fg-3); }
|
||||||
[data-theme="light"] .msk-table tbody tr:hover td{ background:rgba(0,0,0,.035); }
|
[data-theme="light"] .modern-sk-table tbody tr:hover td{ background:rgba(0,0,0,.035); }
|
||||||
|
|
||||||
/* ---------- SCROLL AREA (Radix) ---------- */
|
/* ---------- SCROLL AREA (Radix) ---------- */
|
||||||
.msk-scroll{ overflow:hidden; }
|
.modern-sk-scroll{ overflow:hidden; }
|
||||||
.msk-scroll__viewport{ width:100%; height:100%; border-radius:inherit; }
|
.modern-sk-scroll__viewport{ width:100%; height:100%; border-radius:inherit; }
|
||||||
.msk-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{ display:flex; user-select:none; touch-action:none; padding:2px; background:transparent; transition:background var(--dur-base) var(--ease-out); }
|
||||||
.msk-scroll__bar[data-orientation="vertical"]{ width:10px; }
|
.modern-sk-scroll__bar[data-orientation="vertical"]{ width:10px; }
|
||||||
.msk-scroll__bar[data-orientation="horizontal"]{ flex-direction:column; height:10px; }
|
.modern-sk-scroll__bar[data-orientation="horizontal"]{ flex-direction:column; height:10px; }
|
||||||
.msk-scroll:hover .msk-scroll__bar{ background:rgba(0,0,0,.18); }
|
.modern-sk-scroll:hover .modern-sk-scroll__bar{ background:rgba(0,0,0,.18); }
|
||||||
.msk-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{ flex:1; background:var(--steel-500); border-radius:var(--r-pill); position:relative; box-shadow:0 1px 0 rgba(255,255,255,.08) inset; }
|
||||||
.msk-scroll__thumb:hover{ background:var(--steel-400); }
|
.modern-sk-scroll__thumb:hover{ background:var(--steel-400); }
|
||||||
.msk-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__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) ----------
|
/* ---------- DIALOG / MODAL (Radix Dialog + AlertDialog) ----------
|
||||||
The floating sheet: window chrome aesthetic, scrim with blur. */
|
The floating sheet: window chrome aesthetic, scrim with blur. */
|
||||||
.msk-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{ position:fixed; inset:0; z-index:80; background:rgba(8,9,6,.55); backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); }
|
||||||
.msk-overlay[data-state="open"]{ animation:msk-fade-in var(--dur-base) var(--ease-out); }
|
.modern-sk-overlay[data-state="open"]{ animation:modern-sk-fade-in var(--dur-base) var(--ease-out); }
|
||||||
.msk-overlay[data-state="closed"]{ animation:msk-fade-out var(--dur-quick) var(--ease-out); }
|
.modern-sk-overlay[data-state="closed"]{ animation:modern-sk-fade-out var(--dur-quick) var(--ease-out); }
|
||||||
.msk-dialog{ position:fixed; z-index:81; top:50%; left:50%; transform: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; }
|
.modern-sk-dialog{ position:fixed; z-index:81; top:50%; left:50%; transform: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; }
|
||||||
.msk-dialog[data-state="open"]{ animation:msk-scale-in var(--dur-base) var(--ease-out); }
|
.modern-sk-dialog[data-state="open"]{ animation:modern-sk-scale-in var(--dur-base) var(--ease-out); }
|
||||||
.msk-dialog[data-state="closed"]{ animation:msk-scale-out var(--dur-quick) var(--ease-out); }
|
.modern-sk-dialog[data-state="closed"]{ animation:modern-sk-scale-out var(--dur-quick) var(--ease-out); }
|
||||||
.msk-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__title{ font-family:var(--font-sans); font-size:var(--text-lg); font-weight:600; color:var(--fg-1); letter-spacing:var(--track-snug); }
|
||||||
.msk-dialog__desc{ margin-top:8px; font-size:14px; line-height:1.55; color:var(--fg-2); }
|
.modern-sk-dialog__desc{ margin-top:8px; font-size:14px; line-height:1.55; color:var(--fg-2); }
|
||||||
.msk-dialog__body{ margin-top:16px; }
|
.modern-sk-dialog__body{ margin-top:16px; }
|
||||||
.msk-dialog__footer{ display:flex; justify-content:flex-end; gap:12px; margin-top:22px; }
|
.modern-sk-dialog__footer{ display:flex; justify-content:flex-end; gap:12px; margin-top:22px; }
|
||||||
.msk-dialog__close{ position:absolute; top:14px; right:14px; }
|
.modern-sk-dialog__close{ position:absolute; top:14px; right:14px; }
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
LIGHT THEME — component overrides (ported 1:1)
|
LIGHT THEME — component overrides (ported 1:1)
|
||||||
============================================================ */
|
============================================================ */
|
||||||
[data-theme="light"] .msk-btn--ghost:hover{ background:rgba(0,0,0,.05); color:var(--fg-1); }
|
[data-theme="light"] .modern-sk-btn--ghost:hover{ background:rgba(0,0,0,.05); color:var(--fg-1); }
|
||||||
[data-theme="light"] .msk-btn--ghost:active{ background:rgba(0,0,0,.08); }
|
[data-theme="light"] .modern-sk-btn--ghost:active{ background:rgba(0,0,0,.08); }
|
||||||
[data-theme="light"] .msk-row:hover{ background:rgba(0,0,0,.035); }
|
[data-theme="light"] .modern-sk-row:hover{ background:rgba(0,0,0,.035); }
|
||||||
|
|
||||||
[data-theme="light"] .msk-btn--ember{
|
[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);
|
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"] .msk-switch__thumb{
|
[data-theme="light"] .modern-sk-switch__thumb{
|
||||||
background:linear-gradient(180deg,#ffffff,#e7e7dd);
|
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;
|
box-shadow:0 1px 2px rgba(0,0,0,.22), 0 1px 0 rgba(255,255,255,.9) inset;
|
||||||
}
|
}
|
||||||
[data-theme="light"] .msk-switch{ background:var(--steel-600); }
|
[data-theme="light"] .modern-sk-switch{ background:var(--steel-600); }
|
||||||
[data-theme="light"] .msk-switch[data-state="checked"]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); }
|
[data-theme="light"] .modern-sk-switch[data-state="checked"]{ background:linear-gradient(180deg,var(--lime),var(--lime-deep)); }
|
||||||
|
|
||||||
[data-theme="light"] .msk-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-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"] .msk-menu{ background:rgba(246,246,239,.82); border-color:var(--hair-strong); }
|
[data-theme="light"] .modern-sk-menu{ background:rgba(246,246,239,.82); border-color:var(--hair-strong); }
|
||||||
[data-theme="light"] .msk-select__content{ background:rgba(246,246,239,.82); }
|
[data-theme="light"] .modern-sk-select__content{ background:rgba(246,246,239,.82); }
|
||||||
[data-theme="light"] .msk-tooltip{ background:rgba(246,246,239,.94); color:var(--fg-1); }
|
[data-theme="light"] .modern-sk-tooltip{ background:rgba(246,246,239,.94); color:var(--fg-1); }
|
||||||
|
|
||||||
[data-theme="light"] .msk-titlebar{ border-bottom-color:rgba(0,0,0,.12); }
|
[data-theme="light"] .modern-sk-titlebar{ border-bottom-color:rgba(0,0,0,.12); }
|
||||||
[data-theme="light"] .msk-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-traffic{ box-shadow:0 1px 1px rgba(0,0,0,.18) inset, 0 0 0 .5px rgba(0,0,0,.14); }
|
||||||
|
|
||||||
[data-theme="light"] .msk-overlay{ background:rgba(60,62,52,.32); }
|
[data-theme="light"] .modern-sk-overlay{ background:rgba(60,62,52,.32); }
|
||||||
[data-theme="light"] .msk-scroll:hover .msk-scroll__bar{ background:rgba(0,0,0,.06); }
|
[data-theme="light"] .modern-sk-scroll:hover .modern-sk-scroll__bar{ background:rgba(0,0,0,.06); }
|
||||||
[data-theme="light"] .msk-table thead th{ box-shadow:0 1px 0 rgba(255,255,255,.6) inset; }
|
[data-theme="light"] .modern-sk-table thead th{ box-shadow:0 1px 0 rgba(255,255,255,.6) inset; }
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/* ============================================================
|
||||||
|
ModernSK — optional branded webfonts.
|
||||||
|
Import this ONLY if you want the default ModernSK typefaces:
|
||||||
|
|
||||||
|
import 'modern-sk/styles.css'; // required — tokens + components
|
||||||
|
import 'modern-sk/fonts.css'; // optional — branded faces
|
||||||
|
|
||||||
|
Skip it and the --font-* tokens degrade to system-ui, or
|
||||||
|
override --font-display / --font-sans / --font-mono in your own
|
||||||
|
CSS to map components onto fonts your app already loads.
|
||||||
|
------------------------------------------------------------
|
||||||
|
Onest + Geist Mono come from Google Fonts; Anta (the squared
|
||||||
|
display face) is self-hosted and inlined at build time.
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Onest:wght@300;400;500;600;700;800&family=Geist+Mono:wght@400;500;600&display=swap');
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Anta';
|
||||||
|
src: url('../assets/Anta-Regular.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
@import './fonts.css';
|
||||||
@import './tokens.css';
|
@import './tokens.css';
|
||||||
@import './components.css';
|
@import './components.css';
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
/* ============================================================
|
/* ============================================================
|
||||||
ModernSK UI — shippable stylesheet.
|
ModernSK — shippable stylesheet.
|
||||||
Tokens + component styles only. No demo/page layout, no global
|
Tokens + component styles only. No demo/page layout, no global
|
||||||
reset of consumer elements. Font is inlined at build time.
|
reset of consumer elements. No fonts: import 'modern-sk/fonts.css'
|
||||||
|
for the branded faces, or override the --font-* tokens.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
@import './tokens.css';
|
@import './tokens.css';
|
||||||
@import './components.css';
|
@import './components.css';
|
||||||
|
|
||||||
/* Non-invasive box-sizing for our own components only (zero specificity). */
|
/* Non-invasive box-sizing for our own components only (zero specificity). */
|
||||||
:where([class^='msk-'], [class*=' msk-']) {
|
:where([class^='modern-sk-'], [class*=' modern-sk-']) {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-22
@@ -4,19 +4,14 @@
|
|||||||
Old-iOS skeuomorphism × macOS Sequoia neatness × Ubuntu warmth.
|
Old-iOS skeuomorphism × macOS Sequoia neatness × Ubuntu warmth.
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Single source of truth. Every component reads from here.
|
Single source of truth. Every component reads from here.
|
||||||
|
------------------------------------------------------------
|
||||||
|
Fonts are NOT loaded here. The branded faces (Anta / Onest /
|
||||||
|
Geist Mono) live in the optional `modern-sk/fonts.css`. The
|
||||||
|
--font-* chains below degrade to system-ui when that file is
|
||||||
|
not imported, and an app can override the tokens to remap any
|
||||||
|
typeface without touching this file.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Onest:wght@300;400;500;600;700;800&family=Geist+Mono:wght@400;500;600&display=swap');
|
|
||||||
|
|
||||||
/* Anta — squared geometric display face (self-hosted from /public/fonts). */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Anta';
|
|
||||||
src: url('../assets/Anta-Regular.ttf') format('truetype');
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* ---------- BRAND ---------- */
|
/* ---------- BRAND ---------- */
|
||||||
--lime: #bef264;
|
--lime: #bef264;
|
||||||
@@ -192,7 +187,7 @@
|
|||||||
/* ============================================================
|
/* ============================================================
|
||||||
THE FELT — the one global background.
|
THE FELT — the one global background.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
.msk-felt {
|
.modern-sk-felt {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--ink);
|
background-color: var(--ink);
|
||||||
background-image: var(--bg-glow);
|
background-image: var(--bg-glow);
|
||||||
@@ -201,7 +196,7 @@
|
|||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
color: var(--fg-1);
|
color: var(--fg-1);
|
||||||
}
|
}
|
||||||
.msk-felt::before {
|
.modern-sk-felt::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
@@ -277,40 +272,40 @@
|
|||||||
/* ============================================================
|
/* ============================================================
|
||||||
SEMANTIC TYPE CLASSES
|
SEMANTIC TYPE CLASSES
|
||||||
============================================================ */
|
============================================================ */
|
||||||
.msk-display {
|
.modern-sk-display {
|
||||||
font-family: var(--font-display); font-weight: 400;
|
font-family: var(--font-display); font-weight: 400;
|
||||||
font-size: var(--text-5xl); line-height: var(--lh-tight);
|
font-size: var(--text-5xl); line-height: var(--lh-tight);
|
||||||
letter-spacing: -0.01em; color: var(--fg-1);
|
letter-spacing: -0.01em; color: var(--fg-1);
|
||||||
}
|
}
|
||||||
.msk-h1 {
|
.modern-sk-h1 {
|
||||||
font-family: var(--font-sans); font-weight: var(--w-bold);
|
font-family: var(--font-sans); font-weight: var(--w-bold);
|
||||||
font-size: var(--text-3xl); line-height: var(--lh-snug);
|
font-size: var(--text-3xl); line-height: var(--lh-snug);
|
||||||
letter-spacing: var(--track-snug); color: var(--fg-1);
|
letter-spacing: var(--track-snug); color: var(--fg-1);
|
||||||
}
|
}
|
||||||
.msk-h2 {
|
.modern-sk-h2 {
|
||||||
font-family: var(--font-sans); font-weight: var(--w-semibold);
|
font-family: var(--font-sans); font-weight: var(--w-semibold);
|
||||||
font-size: var(--text-2xl); line-height: var(--lh-snug);
|
font-size: var(--text-2xl); line-height: var(--lh-snug);
|
||||||
letter-spacing: var(--track-snug); color: var(--fg-1);
|
letter-spacing: var(--track-snug); color: var(--fg-1);
|
||||||
}
|
}
|
||||||
.msk-h3 {
|
.modern-sk-h3 {
|
||||||
font-family: var(--font-sans); font-weight: var(--w-semibold);
|
font-family: var(--font-sans); font-weight: var(--w-semibold);
|
||||||
font-size: var(--text-xl); line-height: var(--lh-snug); color: var(--fg-1);
|
font-size: var(--text-xl); line-height: var(--lh-snug); color: var(--fg-1);
|
||||||
}
|
}
|
||||||
.msk-body {
|
.modern-sk-body {
|
||||||
font-family: var(--font-sans); font-weight: var(--w-regular);
|
font-family: var(--font-sans); font-weight: var(--w-regular);
|
||||||
font-size: var(--text-base); line-height: var(--lh-normal); color: var(--fg-2);
|
font-size: var(--text-base); line-height: var(--lh-normal); color: var(--fg-2);
|
||||||
}
|
}
|
||||||
.msk-body-strong { font-weight: var(--w-medium); color: var(--fg-1); }
|
.modern-sk-body-strong { font-weight: var(--w-medium); color: var(--fg-1); }
|
||||||
.msk-caption {
|
.modern-sk-caption {
|
||||||
font-family: var(--font-sans); font-weight: var(--w-medium);
|
font-family: var(--font-sans); font-weight: var(--w-medium);
|
||||||
font-size: var(--text-sm); color: var(--fg-3);
|
font-size: var(--text-sm); color: var(--fg-3);
|
||||||
}
|
}
|
||||||
.msk-label {
|
.modern-sk-label {
|
||||||
font-family: var(--font-sans); font-weight: var(--w-semibold);
|
font-family: var(--font-sans); font-weight: var(--w-semibold);
|
||||||
font-size: 11px; text-transform: uppercase;
|
font-size: 11px; text-transform: uppercase;
|
||||||
letter-spacing: var(--track-caps); color: var(--fg-3);
|
letter-spacing: var(--track-caps); color: var(--fg-3);
|
||||||
}
|
}
|
||||||
.msk-mono {
|
.modern-sk-mono {
|
||||||
font-family: var(--font-mono); font-weight: var(--w-regular);
|
font-family: var(--font-mono); font-weight: var(--w-regular);
|
||||||
font-size: var(--text-sm); color: var(--fg-2);
|
font-size: var(--text-sm); color: var(--fg-2);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user