first commit

This commit is contained in:
mstfyldz
2026-08-09 11:20:13 +03:00
commit 916457f431
49 changed files with 14336 additions and 0 deletions
@@ -0,0 +1,152 @@
{
"plugins": [
"react",
"import"
],
"rules": {
"react/forbid-elements": [
"warn",
{
"forbid": []
}
],
"no-restricted-imports": [
"warn",
{
"patterns": []
}
],
"no-restricted-syntax": [
"warn",
{
"selector": "Literal[value=/#[0-9a-fA-F]{3,8}\\b/]",
"message": "Raw hex color — use a design-system color token via var()."
},
{
"selector": "Literal[value=/\\b\\d+px\\b/]",
"message": "Raw px value — use a design-system spacing token via var()."
},
{
"selector": "Literal[value=/font-family\\s*:\\s*(?!['\\\"]?(?:Cormorant Garamond|Lora))/i]",
"message": "Font not provided by the design system. Available: Cormorant Garamond, Lora."
}
]
},
"overrides": [
{
"files": [
"**/index.js"
],
"rules": {
"no-restricted-imports": "off"
}
}
],
"x-omelette": {
"components": {},
"tokens": [
"--color-accent",
"--color-accent-100",
"--color-accent-2",
"--color-accent-2-100",
"--color-accent-2-200",
"--color-accent-2-300",
"--color-accent-2-400",
"--color-accent-2-500",
"--color-accent-2-600",
"--color-accent-2-700",
"--color-accent-2-800",
"--color-accent-2-900",
"--color-accent-200",
"--color-accent-300",
"--color-accent-400",
"--color-accent-500",
"--color-accent-600",
"--color-accent-700",
"--color-accent-800",
"--color-accent-900",
"--color-bg",
"--color-divider",
"--color-neutral-100",
"--color-neutral-200",
"--color-neutral-300",
"--color-neutral-400",
"--color-neutral-500",
"--color-neutral-600",
"--color-neutral-700",
"--color-neutral-800",
"--color-neutral-900",
"--color-surface",
"--color-text",
"--font-body",
"--font-heading",
"--font-heading-weight",
"--radius-lg",
"--radius-md",
"--radius-sm",
"--shadow-lg",
"--shadow-md",
"--shadow-sm",
"--space-1",
"--space-2",
"--space-3",
"--space-4",
"--space-6",
"--space-8"
],
"tokenKinds": {
"--color-bg": "color",
"--color-surface": "color",
"--color-text": "font",
"--color-accent": "color",
"--color-accent-2": "color",
"--color-divider": "color",
"--color-neutral-100": "color",
"--color-neutral-200": "color",
"--color-neutral-300": "color",
"--color-neutral-400": "color",
"--color-neutral-500": "color",
"--color-neutral-600": "color",
"--color-neutral-700": "color",
"--color-neutral-800": "color",
"--color-neutral-900": "color",
"--color-accent-100": "color",
"--color-accent-200": "color",
"--color-accent-300": "color",
"--color-accent-400": "color",
"--color-accent-500": "color",
"--color-accent-600": "color",
"--color-accent-700": "color",
"--color-accent-800": "color",
"--color-accent-900": "color",
"--color-accent-2-100": "color",
"--color-accent-2-200": "color",
"--color-accent-2-300": "color",
"--color-accent-2-400": "color",
"--color-accent-2-500": "color",
"--color-accent-2-600": "color",
"--color-accent-2-700": "color",
"--color-accent-2-800": "color",
"--color-accent-2-900": "color",
"--font-heading": "font",
"--font-heading-weight": "font",
"--font-body": "font",
"--space-1": "spacing",
"--space-2": "spacing",
"--space-3": "spacing",
"--space-4": "spacing",
"--space-6": "spacing",
"--space-8": "spacing",
"--radius-sm": "radius",
"--radius-md": "radius",
"--radius-lg": "radius",
"--shadow-sm": "shadow",
"--shadow-md": "shadow",
"--shadow-lg": "shadow"
},
"fontFamilies": [
"Cormorant Garamond",
"Lora"
]
}
}
@@ -0,0 +1,11 @@
/* @ds-bundle: {"format":4,"namespace":"Classical_classi","components":[],"sourceHashes":{},"inlinedExternals":[],"unexposedExports":[]} */
(() => {
const __ds_ns = (window.Classical_classi = window.Classical_classi || {});
const __ds_scope = {};
(__ds_ns.__errors = __ds_ns.__errors || []);
})();
File diff suppressed because one or more lines are too long
@@ -0,0 +1,81 @@
# Classical design system
Classical is an editorial, book-like system on a soft near-white ground: Cormorant Garamond headings over Lora body, justified columns, hairline rules, and color applied as stroke rather than fill. Surfaces stay quiet — cards are bordered, buttons are outlined — and photographs sit matted on the page like tipped-in plates.
## How to use this
- Link the one stylesheet from every page — `<link rel="stylesheet" href="styles.css">` (adjust the relative path) — and take every color, font, spacing, radius and shadow from its variables (`var(--color-*)`, `var(--font-*)`, `var(--space-*)`, `var(--radius-*)`, `var(--shadow-*)`). Never hard-code a hex, a font name or a px value the tokens already carry.
- Build with the classes below rather than inventing parallel ones; the component pages are plain HTML, so view source and copy the markup.
- `templates/` holds starting points a consuming project can copy whole.
- The whole system was derived from `theme.json`. To change the look, edit the tokens at the top of `styles.css` — every page, the thumbnail and this guide read from them — and keep `theme.json` and the written guidance in step so they don't drift from what the CSS actually does.
## Direction
Editorial, justified body copy in columns. Tight leading; headlines flush-left over justified text blocks. Use hairline dividers (`var(--color-divider)`) between major sections. Apply color as borders, rules and underlines — not as filled blocks. Buttons are outlined (1px accent border on transparent), not solid-filled. Wrap hero and inline images in the `.plate` class — a warm archival grade inside a thin surface-colored mat, like a tipped-in book plate. In decks, section dividers sit on a deep warm near-black (a shade below `--color-neutral-900`) as a colophon page — paper type with a gold ghost numeral; gold stays stroke and small marks on content slides.
## Color
A light ground (`--color-bg` #f3f2f2) with `--color-text` #201f1d and a single accent #b68235 (this is a mono scheme: no second accent was chosen — the `--color-accent-2-*` variables carry a machine-derived stand-in kept only so both sets resolve; treat them as one role). Each role carries a 100900 tonal ramp (`--color-neutral-100``--color-accent-2-900`) generated in OKLCH on a shared perceptual lightness scale, so the same step of any ramp has the same visual weight. Use the light steps (100300) for tinted fills, hovers and subtle borders, 500 as the role's base, and the dark steps (700900) for text on tinted fills and for pressed states; prefer ramp steps over ad-hoc `color-mix()`. For elevation use `--shadow-sm/md/lg` (already tuned to the ground) rather than ad-hoc box-shadows.
## Type
Cormorant Garamond for headings over Lora for body text, loaded as `--font-heading` / `--font-body`. Bold is avoided: interface headings cap at semibold (the `--font-heading-weight` token), and the bigger the text the lighter it sets — display sizes take the normal cut (the deck shows this). Numbers set tabular wherever they stand as figures or columns — kickers, contents numbers, tables, charts, the display numerals (`"tnum"`; both faces keep their own figure style and gain equal widths) — while running prose keeps its text figures (Lora's tabular feature also widens its word-spaces and punctuation, which would loosen the prose). Density 1.15× and radius 4px are already baked into the `--space-*` / `--radius-*` scales — use the variables, not raw numbers.
## Icons
Use Lucide icons (https://lucide.dev) throughout.
## Interaction states
Interactive states are themed, never browser defaults: give every interactive element a `:hover` tint and a pressed state from the accent ramp (one step past the base — `--color-accent-600` on a light ground, `--color-accent-400` on a dark one, or a `color-mix()` tint for outlined/ghost variants), and style keyboard focus with `:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }` — never leave the default blue focus ring.
## Components
| Class | What it is | Shown in |
| --- | --- | --- |
| `.btn` with `.btn-primary`, `.btn-secondary`, `.btn-ghost`, `.btn-icon`, `.btn-block` | Actions — the primary is an accent outline, never a fill | components/buttons.html |
| `.tag` with `.tag-accent`, `.tag-accent-2`, `.tag-neutral`, `.tag-outline` | Small labels tinted from the ramps (mono palette: accent-2 reads the same as accent) | components/buttons.html |
| `.field` + `label`, `.input`, `.radio` + `.dot`, `.seg` + `.seg-opt` | Form fields and choices on native elements — no script | components/forms.html |
| `.card` with `.card-kicker`, `.card-title`, `.card-body`, `.card-meta`; `.elev-sm/md/lg` | Bordered, unfilled content surfaces; elevation utilities | components/cards.html |
| `.nav` + `.nav-brand` | The header bar | components/navigation.html |
| `.table` | Data tables with themed header and row rules | components/table.html |
| `.dialog-backdrop` + `.dialog` (+ `.dialog-title/-body/-actions`) | A modal at the top elevation | components/dialog.html |
| `.hr` | A hairline horizontal rule | foundations/layout.html |
| `.plate` | The image wrapper — every content photograph goes through it | foundations/image.html |
States are built in: hovers and pressed states come from the accent ramp, keyboard focus is the 2px accent `:focus-visible` ring, `::selection` is an accent tint, and disabled controls drop to 45% opacity. Don't restyle them per page. The accent-to-ground pair is tuned to at least 3:1 — enough for icons, large text and interface chrome, not for body copy — so for paragraph-size text in the accent use a deep ramp step (`--color-accent-700` on this ground) rather than the accent itself.
## Do
- Justify body copy at a comfortable measure and let the hairlines carry the structure.
- Draw with borders, rules and underlines; keep large fills off the page.
- Give text room — the spacing scale is airy (density 1.15×) by design.
- Mat photographs with the `.plate` wrapper so they read as plates, not banners.
## Don't
- Do not fill cards or buttons with solid accent color.
- Do not use heavy drop shadows — elevation here is a whisper.
- Do not tighten the leading or crowd the margins.
- Do not swap in a sans-serif for emphasis; weight and italics do that job.
## Files
- `styles.css` — the only stylesheet: the token sheet (`:root` variables, ramps, base type) plus the component layer. Link it from every page.
- `readme.md` — this guide.
- `theme.json` — the parameters these files were derived from (a machine-readable record of the theme).
- `thumbnail.html` — the project cover (brand mark + swatches).
- `foundations/type.html` — the type scale and the heading/body pairing at real sizes.
- `foundations/color.html` — color roles and the 100-900 tonal ramps, with usage notes.
- `foundations/layout.html` — the spacing scale, the grid and how edges are drawn.
- `foundations/icons.html` — the icon set at interface sizes, inline and in buttons.
- `foundations/image.html` — how photographs and figures are treated.
- `components/buttons.html` — buttons, icon buttons and tags in every variant and state.
- `components/forms.html` — text fields, radios and the segmented control on native elements.
- `components/cards.html` — content cards and the elevation steps.
- `components/navigation.html` — the header bar pattern.
- `components/table.html` — a data table with the themed header and row rules.
- `components/dialog.html` — a modal over its backdrop at the top elevation.
- `theme.html` — the theme's parameters rendered as a reference sheet.
- `templates/landing/` — a starter page consuming the system the intended way (`index.html`, its `ds-base.js` loader, and the vendored `image-slot.js` its photograph mounts).
- `assets/photo.jpg` — the reference photograph the imagery page treats.
@@ -0,0 +1,255 @@
/* Classical — design-system tokens and component classes. This file is the source of truth for the system's look; retune it here and see readme.md. */
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;600&family=Lora:wght@400;600&display=swap');
:root {
--color-bg: #f3f2f2;
--color-surface: #eae9e9;
--color-text: #201f1d;
--color-accent: #b68235;
--color-accent-2: #ac803e;
--color-divider: color-mix(in srgb, #201f1d 16%, transparent);
/* Tonal ramps — generated in OKLCH on one shared lightness scale, so the
same step of any role matches the others in visual value. */
--color-neutral-100: #f8f4f4;
--color-neutral-200: #eae7e7;
--color-neutral-300: #d7d3d3;
--color-neutral-400: #bab6b6;
--color-neutral-500: #9b9797;
--color-neutral-600: #7d7979;
--color-neutral-700: #605d5d;
--color-neutral-800: #444141;
--color-neutral-900: #2d2b2b;
--color-accent-100: #fff3e4;
--color-accent-200: #ffe3bf;
--color-accent-300: #facb8d;
--color-accent-400: #e1ad66;
--color-accent-500: #c28d41;
--color-accent-600: #a06f24;
--color-accent-700: #7d5411;
--color-accent-800: #5a3b0a;
--color-accent-900: #3a270d;
--color-accent-2-100: #fff3e4;
--color-accent-2-200: #ffe3be;
--color-accent-2-300: #f5cd96;
--color-accent-2-400: #dbaf70;
--color-accent-2-500: #bc8f4e;
--color-accent-2-600: #9b7232;
--color-accent-2-700: #79561f;
--color-accent-2-800: #573d14;
--color-accent-2-900: #382810;
--font-heading: "Cormorant Garamond", system-ui, sans-serif;
--font-heading-weight: 600; /* review round 3: bold retired — semibold is the
interface headings' ceiling (they need the weight at small sizes); display
text goes lighter still, to the normal cut (see the deck) */
--font-body: "Lora", system-ui, sans-serif;
--space-1: 4.6px;
--space-2: 9.2px;
--space-3: 13.8px;
--space-4: 18.4px;
--space-6: 27.6px;
--space-8: 36.8px;
--radius-sm: 2px;
--radius-md: 4px;
--radius-lg: 7px;
/* Elevation — derived from the ground: soft ink-tinted shadows on a
light theme, a hairline edge + ambient darkness on a dark one. */
--shadow-sm: 0 1px 2px color-mix(in srgb, #2d2b2b 14%, transparent);
--shadow-md: 0 3px 10px color-mix(in srgb, #2d2b2b 16%, transparent);
--shadow-lg: 0 12px 32px color-mix(in srgb, #2d2b2b 22%, transparent);
}
body {
background: var(--color-bg);
color: var(--color-text);
font-family: var(--font-body);
}
h1, h2, h3, h4 { font-family: var(--font-heading); font-weight: var(--font-heading-weight); }
.plate{filter:sepia(0.22) saturate(0.82) contrast(1.05);box-sizing:border-box;
border:6px solid var(--color-surface);outline:1px solid var(--color-divider)}
/* ══════════════════════════════════════════════════════════════════════════
Components — built with the tokens above. Plain CSS
on plain HTML: no JavaScript, no build step. Each class is documented in
readme.md and demonstrated in foundations/ and components/.
══════════════════════════════════════════════════════════════════════ */
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-size: 15px; line-height: 1.55; font-weight: 400; }
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
line-height: 1.12; letter-spacing: -0.015em; margin: 0 0 var(--space-2);
}
h1 { font-size: 42px; }
h2 { font-size: 32px; }
h3 { font-size: 25px; }
h4 { font-size: 20px; }
h5 { font-size: 16px; }
h6 { font-size: 13px; }
h6 { letter-spacing: 0.08em; text-transform: uppercase; }
p { margin: 0 0 var(--space-3); }
a { color: var(--color-accent); text-underline-offset: 3px; }
img { display: block; max-width: 100%; }
figure { margin: 0; }
figcaption {
font-size: 11px; margin-top: var(--space-1);
color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
.text-muted { color: color-mix(in srgb, var(--color-text) 55%, transparent); }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }
/* — rules — */
.hr {
height: 1px; border: 0; margin: var(--space-4) 0;
background: var(--color-divider);
}
/* — buttons — */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
cursor: pointer; text-decoration: none;
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: 14px; line-height: 1.2; color: var(--color-text); /* matches the .input's 14px —
the pair sits side by side in sign-up rows */
background: transparent; border: 1px solid transparent;
padding: var(--space-2) calc(var(--space-3) * 1.2);
border-radius: var(--radius-md);
}
.btn svg { display: block; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary { color: var(--color-accent); border-color: var(--color-accent); }
.btn-primary:hover { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
.btn-primary:active { background: color-mix(in srgb, var(--color-accent) 22%, transparent); }
.btn-secondary { border-color: var(--color-divider); }
.btn-secondary:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }
.btn-ghost { color: var(--color-accent); padding-inline: var(--space-1); }
.btn-ghost:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }
.btn-ghost:active { background: color-mix(in srgb, var(--color-accent) 18%, transparent); }
.btn-icon { width: 36px; height: 36px; padding: 0; }
.btn-block { width: 100%; margin-top: var(--space-2); }
/* — forms — */
.field > label {
display: block; font-size: 12px; margin-bottom: 5px;
color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
.input {
width: 100%; min-height: 36px; padding: 6px 10px; font: inherit;
font-size: 14px; color: var(--color-text); caret-color: var(--color-accent);
background: transparent;
border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.input:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.input:focus-visible { border-color: var(--color-accent); outline-offset: 0; }
textarea.input { min-height: 90px; resize: vertical; }
.radio { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; }
.radio input, .seg-opt input {
position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.radio .dot {
width: 16px; height: 16px; flex: none; border-radius: 50%;
border: 1.5px solid var(--color-divider);
}
.radio:hover .dot { border-color: var(--color-accent); }
.radio input:checked + .dot {
border-color: var(--color-accent); background: var(--color-accent);
box-shadow: inset 0 0 0 4px var(--color-bg);
}
.radio input:focus-visible + .dot { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.seg {
display: inline-flex; overflow: hidden;
border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.seg-opt {
display: inline-flex; align-items: center; gap: 6px;
padding: 7px 12px; font-size: 13px; cursor: pointer;
}
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt:has(input:checked) { color: var(--color-accent); box-shadow: inset 0 0 0 1px var(--color-accent); }
.seg-opt:not(:has(input:checked)):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }
/* — cards — */
.card {
display: flex; flex-direction: column; gap: var(--space-2);
padding: var(--space-3); border-radius: var(--radius-md); background: transparent; border: 1px solid var(--color-divider);
}
.card-kicker { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-accent); }
.card-title {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: 17px; line-height: 1.2;
}
.card-body { margin: 0; font-size: 13px; opacity: 0.8; flex: 1; }
.card-meta {
display: flex; align-items: center; gap: 6px; font-size: 11px;
color: color-mix(in srgb, var(--color-text) 50%, transparent);
}
.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }
/* — tags — */
.tag {
display: inline-flex; align-items: center; font-size: 11px;
letter-spacing: 0.02em; padding: 3px 10px;
border-radius: calc(var(--radius-md) * 0.75);
}
.tag-accent { background: var(--color-accent-100); color: var(--color-accent-800); }
.tag-accent-2 { background: var(--color-accent-2-100); color: var(--color-accent-2-800); }
.tag-neutral { background: var(--color-neutral-100); color: var(--color-neutral-800); }
.tag-outline { border: 1px solid var(--color-accent); color: var(--color-accent); }
/* — navigation — */
.nav {
display: flex; align-items: center; gap: var(--space-4);
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--color-divider);
}
.nav-brand {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: 18px; margin-right: auto;
}
.nav a { color: inherit; text-decoration: none; font-size: 14px; }
.nav a:hover, .nav a[aria-current='page'] { color: var(--color-accent); }
/* — tables — */
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
text-align: left; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
color: color-mix(in srgb, var(--color-text) 60%, transparent);
padding: var(--space-2); border-bottom: 1px solid var(--color-divider);
}
.table td {
padding: var(--space-2);
border-bottom: 1px solid var(--color-divider);
}
.table tbody tr:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
/* — dialog — */
.dialog-backdrop {
position: fixed; inset: 0; display: grid; place-items: center;
padding: var(--space-4);
background: color-mix(in srgb, var(--color-neutral-900) 50%, transparent);
}
.dialog {
width: min(440px, 100%); display: flex; flex-direction: column; gap: var(--space-3);
padding: var(--space-4); border-radius: var(--radius-lg);
background: var(--color-surface); box-shadow: var(--shadow-lg); border: 1px solid var(--color-divider);
}
.dialog-title {
font-family: var(--font-heading); font-weight: var(--font-heading-weight);
font-size: 20px;
}
.dialog-body { font-size: 14px; opacity: 0.85; }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }