Files
whynot-design/src/styles/components.css
tegwick 80252baf53
Some checks failed
ci / check (push) Has been cancelled
ci / release (push) Has been cancelled
version 0.2.0 replaces fromer version!
2026-05-25 19:32:22 +02:00

591 lines
21 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
WhyNot Design System — Component Styles
------------------------------------------------------------
Utility classes that the Lit web components render to. These
are also consumable directly from any HTML (no JS required)
for the "Layer 1 only" use case — see MultiFrameworkSupport.md.
============================================================ */
/* ====== Custom-element display defaults ======
* For shadow-DOM components, the wn-* host has display: inline by default.
* Set sensible defaults so layout works without the consumer specifying them.
*/
wn-eyebrow, wn-tag, wn-stage-dot, wn-phase-dot, wn-stamp, wn-icon,
wn-search-input, wn-button { display: inline-block; }
wn-card, wn-modal, wn-top-nav, wn-sidebar, wn-page-header,
wn-pipeline, wn-prototype-card, wn-field-row, wn-breadcrumb,
wn-table, wn-banner, wn-empty-state,
wn-input, wn-textarea, wn-select { display: block; }
wn-toast-region { display: block; }
wn-toast { display: block; }
wn-sidebar-group, wn-sidebar-item { display: block; }
wn-table-row, wn-table-cell { display: contents; }
/* host hidden state — needed because shadow-DOM components don't inherit
* `[hidden]` semantics in light DOM. Lit's host attribute reflection
* handles attributes, but `hidden` on the host itself should still work. */
[hidden] { display: none !important; }
/* ====== Buttons ====== */
.wn-btn {
font: 500 13px var(--ff-sans);
letter-spacing: -0.005em;
padding: 9px 16px;
border-radius: var(--r-2);
border: 1px solid var(--border);
background: var(--paper);
color: var(--ink);
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
white-space: nowrap;
transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
text-decoration: none;
line-height: 1.2;
}
.wn-btn:hover { border-color: var(--ink); }
.wn-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.wn-btn:active { background: var(--bg-3); }
.wn-btn[disabled], .wn-btn.is-disabled {
color: var(--ink-5); border-color: var(--border); cursor: not-allowed; background: var(--paper);
}
.wn-btn--primary { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.wn-btn--primary:hover { background: var(--ink-2); border-color: var(--ink-2); }
.wn-btn--primary:active { background: var(--ink); }
.wn-btn--primary[disabled], .wn-btn--primary.is-disabled {
background: var(--ink-5); border-color: var(--ink-5); color: var(--paper);
}
.wn-btn--ghost { background: transparent; border-color: transparent; padding: 7px 10px; }
.wn-btn--ghost:hover { background: var(--bg-3); border-color: transparent; }
.wn-btn--danger { background: var(--paper); color: var(--ink); border-color: var(--ink); }
.wn-btn--sm { padding: 5px 10px; font-size: 12px; }
.wn-btn--lg { padding: 12px 20px; font-size: 14px; }
.wn-btn__icon { width: 14px; height: 14px; flex: none; }
.wn-btn--lg .wn-btn__icon { width: 16px; height: 16px; }
/* ====== Eyebrows & labels ====== */
.wn-eyebrow {
font: 500 11px/1.2 var(--ff-mono);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--fg-3);
display: inline-block;
}
.wn-eyebrow--strong { color: var(--fg-1); }
/* ====== Tags ====== */
.wn-tag {
font: 500 10px/1 var(--ff-mono);
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 5px 10px;
border-radius: var(--r-pill);
border: 1px solid var(--border);
color: var(--fg-2);
background: var(--paper);
display: inline-block;
white-space: nowrap;
}
.wn-tag--active { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.wn-tag--draft { background: var(--hi); color: var(--hi-ink); border-color: transparent; }
/* ====== Stage / Phase dots ====== */
.wn-dot {
display: inline-flex;
align-items: center;
gap: 6px;
font: 500 10px/1 var(--ff-mono);
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--fg-2);
}
.wn-dot__bullet { width: 8px; height: 8px; border-radius: 999px; background: var(--ink); flex: none; }
/* signal levels (S0S4) */
.wn-stage-dot--s0 .wn-dot__bullet { background: var(--status-raw); }
.wn-stage-dot--s1 .wn-dot__bullet { background: var(--status-weak); }
.wn-stage-dot--s2 .wn-dot__bullet { background: var(--status-medium); }
.wn-stage-dot--s3 .wn-dot__bullet { background: var(--status-strong); }
.wn-stage-dot--s4 .wn-dot__bullet { background: var(--status-commercial); }
/* phase states (todo / active / done / warn) — numbered phases, distinct from signal */
.wn-phase-dot__bullet {
width: 18px; height: 18px; border-radius: 999px;
border: 1px solid var(--border-strong);
background: var(--paper);
display: inline-flex; align-items: center; justify-content: center;
font: 500 10px/1 var(--ff-mono); color: var(--fg-3);
flex: none;
}
.wn-phase-dot--todo .wn-phase-dot__bullet { border-color: var(--border-strong); color: var(--fg-3); background: var(--paper); }
.wn-phase-dot--active .wn-phase-dot__bullet { border-color: var(--ink); color: var(--ink); background: var(--paper); box-shadow: 0 0 0 3px rgba(10,10,10,0.06); }
.wn-phase-dot--done .wn-phase-dot__bullet { border-color: var(--ink); color: var(--paper); background: var(--ink); }
.wn-phase-dot--warn .wn-phase-dot__bullet { border-color: var(--hi-2); color: var(--hi-ink); background: var(--hi); }
/* ====== Stamp ====== */
.wn-stamp {
display: inline-block;
background: var(--hi);
color: var(--hi-ink);
padding: 5px 10px 3px;
font: 500 10px/1 var(--ff-mono);
letter-spacing: 0.12em;
text-transform: uppercase;
transform: rotate(-1.5deg);
}
/* ====== Icon ====== */
.wn-icon { stroke-width: 1.5; stroke: currentColor; fill: none; display: inline-block; vertical-align: middle; }
.wn-icon--sm { width: 14px; height: 14px; }
.wn-icon--md { width: 16px; height: 16px; }
.wn-icon--lg { width: 20px; height: 20px; }
.wn-icon--xl { width: 24px; height: 24px; }
/* ====== Card ====== */
.wn-card {
background: var(--paper);
border: 1px solid var(--border);
border-radius: var(--r-2);
padding: var(--sp-5);
display: flex;
flex-direction: column;
gap: var(--sp-3);
position: relative;
}
.wn-card--inset { background: var(--paper-2); border-color: var(--border); }
.wn-card--recessed { background: var(--paper-3); }
.wn-card--lg { padding: var(--sp-6); border-radius: var(--r-3); }
.wn-card--sm { padding: var(--sp-4); gap: var(--sp-2); }
.wn-card--clickable { cursor: pointer; transition: border-color 120ms ease; }
.wn-card--clickable:hover { border-color: var(--ink); }
.wn-card--clickable:hover::before {
content: ""; position: absolute; left: -1px; top: -1px; bottom: -1px;
width: 2px; background: var(--ink); border-radius: 2px 0 0 2px;
}
.wn-card__head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-3); }
.wn-card__title { font: 500 17px/1.35 var(--ff-sans); margin: 4px 0 8px; color: var(--fg-1); }
.wn-card__foot {
display: flex; justify-content: space-between; gap: var(--sp-3);
padding-top: var(--sp-3); margin-top: 4px;
border-top: 1px solid var(--border-soft);
font: 500 11px var(--ff-mono); letter-spacing: 0.06em; text-transform: uppercase;
color: var(--fg-3);
}
/* ====== Field row (label + value, 3-col grid) ====== */
.wn-field-row {
display: grid;
grid-template-columns: 200px 1fr auto;
gap: var(--sp-4) var(--sp-5);
padding: var(--sp-3) 0;
border-bottom: 1px solid var(--border-soft);
align-items: baseline;
}
.wn-field-row:last-child { border-bottom: 0; }
.wn-field-row__label {
font: 500 11px/1.5 var(--ff-mono);
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--fg-3);
}
.wn-field-row__value { font: 400 15px/1.55 var(--ff-sans); color: var(--fg-1); }
.wn-field-row__aside { font: 400 12px var(--ff-mono); color: var(--fg-3); text-align: right; }
.wn-field-row--stacked { grid-template-columns: 1fr; gap: 6px; }
.wn-field-row--narrow { grid-template-columns: 120px 1fr; }
/* ====== Form inputs ====== */
.wn-form-label {
font: 500 11px/1 var(--ff-mono);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--fg-3);
display: block;
margin-bottom: 6px;
}
.wn-input, .wn-textarea, .wn-select {
font: 400 14px var(--ff-sans);
padding: 10px 12px;
border: 1px solid var(--border);
background: var(--paper);
border-radius: var(--r-1);
color: var(--fg-1);
outline: none;
width: 100%;
transition: border-color 120ms ease;
box-sizing: border-box;
}
.wn-input:hover, .wn-textarea:hover, .wn-select:hover { border-color: var(--border-strong); }
.wn-input:focus, .wn-textarea:focus, .wn-select:focus { border-color: var(--ink); }
.wn-input::placeholder, .wn-textarea::placeholder { color: var(--ink-5); }
.wn-input[disabled], .wn-textarea[disabled], .wn-select[disabled] {
background: var(--paper-2); color: var(--fg-3); cursor: not-allowed;
}
.wn-textarea { resize: vertical; min-height: 96px; font-family: var(--ff-sans); }
.wn-select {
appearance: none; -webkit-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none' stroke='%235C5C5C' stroke-width='1.5'><path d='M1 1l4 4 4-4'/></svg>");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 32px;
}
.wn-input--error, .wn-textarea--error, .wn-select--error {
border-color: var(--ink); border-bottom-width: 2px;
}
.wn-form-help { font: 400 11px var(--ff-mono); color: var(--fg-3); margin-top: 6px; display: block; }
.wn-form-error { font: 400 11px var(--ff-mono); color: var(--ink); margin-top: 6px; display: block; }
/* Search input — extracted from TopNav, also usable standalone */
.wn-search {
display: inline-flex;
align-items: center;
gap: 10px;
border: 1px solid var(--border);
padding: 6px 10px;
border-radius: var(--r-1);
background: var(--paper);
color: var(--fg-3);
font: 400 12px var(--ff-mono);
min-width: 200px;
transition: border-color 120ms ease;
}
.wn-search:focus-within { border-color: var(--ink); }
.wn-search input {
border: 0; outline: 0; background: none; flex: 1;
font: inherit; color: var(--fg-1); padding: 0;
}
.wn-search input::placeholder { color: var(--ink-5); }
.wn-search__kbd {
padding: 1px 5px;
border: 1px solid var(--border);
border-radius: 2px;
font-size: 10px;
color: var(--fg-3);
}
/* ====== Breadcrumb ====== */
.wn-breadcrumb {
display: flex; flex-wrap: wrap; align-items: center;
gap: 6px;
font: 400 12px/1.5 var(--ff-mono);
color: var(--fg-3);
margin-bottom: var(--sp-4);
}
.wn-breadcrumb a {
color: var(--fg-2);
text-decoration: none;
padding: 2px 0;
border-bottom: 1px solid transparent;
transition: border-color 120ms ease, color 120ms ease;
}
.wn-breadcrumb a:hover { color: var(--fg-1); border-bottom-color: var(--border-strong); }
.wn-breadcrumb__sep { color: var(--ink-5); user-select: none; }
.wn-breadcrumb__current { color: var(--fg-1); }
/* ====== Modal / Dialog ====== */
.wn-modal__backdrop {
position: fixed; inset: 0;
background: rgba(10, 10, 10, 0.40);
display: flex; align-items: center; justify-content: center;
z-index: 100;
padding: var(--sp-5);
}
.wn-modal__panel {
background: var(--paper);
border-radius: var(--r-3);
box-shadow: var(--shadow-3);
max-width: 560px; width: 100%;
max-height: calc(100vh - 64px);
display: flex; flex-direction: column;
overflow: hidden;
}
.wn-modal__head {
padding: var(--sp-5) var(--sp-6) var(--sp-4);
border-bottom: 1px solid var(--border);
display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4);
}
.wn-modal__title { font: 500 20px/1.25 var(--ff-sans); margin: 0; color: var(--fg-1); }
.wn-modal__close {
background: none; border: 0; cursor: pointer; padding: 4px;
color: var(--fg-3); border-radius: var(--r-1);
transition: color 120ms ease;
}
.wn-modal__close:hover { color: var(--fg-1); }
.wn-modal__body {
padding: var(--sp-5) var(--sp-6);
overflow-y: auto;
flex: 1;
font: 400 15px/1.6 var(--ff-sans);
color: var(--fg-1);
}
.wn-modal__foot {
padding: var(--sp-4) var(--sp-6) var(--sp-5);
border-top: 1px solid var(--border);
display: flex; justify-content: flex-end; gap: var(--sp-2);
}
/* ====== Table ======
* Note: shadow-DOM-rendered rows can't be children of a real <table> (the
* HTML table model rejects unknown elements between <table> and <tr>). The
* <wn-table> component therefore renders a CSS-grid imitation. For real
* <table> markup (Django QuerySet rendering, etc.) use these classes
* directly on <table>/<tr>/<td> elements — see also the .wn-table--native
* variant below.
*/
/* CSS-grid imitation (default <wn-table>) */
.wn-table {
width: 100%;
font-size: var(--fs-sm);
display: flex;
flex-direction: column;
}
.wn-table__thead { border-bottom: 1px solid var(--border); }
.wn-table__tbody { display: flex; flex-direction: column; }
.wn-table__tr {
display: grid;
gap: var(--sp-4);
padding: var(--sp-3) var(--sp-4);
border-bottom: 1px solid var(--border-soft);
align-items: baseline;
}
.wn-table__tr:last-child { border-bottom: 0; }
.wn-table__tr--head { border-bottom: 0; padding: var(--sp-3) var(--sp-4); }
.wn-table__th {
font: 500 11px/1.2 var(--ff-mono);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--fg-3);
}
.wn-table__td {
color: var(--fg-1);
line-height: 1.5;
font-size: var(--fs-sm);
}
.wn-table--compact .wn-table__tr { padding: var(--sp-2) var(--sp-3); }
.wn-table__cell--mono { font-family: var(--ff-mono); color: var(--fg-2); font-size: 12px; }
.wn-table__cell--meta { color: var(--fg-3); font: 400 12px var(--ff-mono); }
.wn-table__cell--right { text-align: right; }
/* Native <table> variant — for Django QuerySet rendering etc. */
.wn-table--native {
border-collapse: collapse;
display: table;
}
.wn-table--native thead th {
text-align: left;
padding: var(--sp-3) var(--sp-4);
border-bottom: 1px solid var(--border);
font: 500 11px/1.2 var(--ff-mono);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--fg-3);
}
.wn-table--native tbody td {
padding: var(--sp-4);
border-bottom: 1px solid var(--border-soft);
vertical-align: top;
color: var(--fg-1);
font-size: var(--fs-sm);
line-height: 1.5;
}
.wn-table--native tbody tr:hover { background: var(--paper-2); }
.wn-table--native tbody tr:last-child td { border-bottom: 0; }
/* ====== Banner / Toast (success / info / warn) ====== */
.wn-banner {
display: flex;
align-items: flex-start;
gap: var(--sp-3);
padding: var(--sp-3) var(--sp-4);
border: 1px solid var(--border);
background: var(--paper);
border-radius: var(--r-2);
font: 400 14px/1.5 var(--ff-sans);
color: var(--fg-1);
position: relative;
}
.wn-banner__icon { color: var(--fg-2); flex: none; padding-top: 2px; }
.wn-banner__body { flex: 1; }
.wn-banner__title {
font: 500 11px/1.2 var(--ff-mono);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--fg-3);
margin: 0 0 4px;
}
.wn-banner__dismiss {
background: none; border: 0; cursor: pointer;
color: var(--fg-3); padding: 4px;
}
.wn-banner__dismiss:hover { color: var(--fg-1); }
.wn-banner--success { border-left: 2px solid var(--ink); }
.wn-banner--warn { border-left: 2px solid var(--hi-2); background: #FFFCEB; }
.wn-banner--error { border-left: 2px solid var(--ink); background: var(--paper); }
.wn-banner--info { border-left: 2px solid var(--border-strong); }
.wn-toast-region {
position: fixed;
bottom: var(--sp-5); right: var(--sp-5);
display: flex; flex-direction: column; gap: var(--sp-2);
z-index: 200;
max-width: 380px;
}
.wn-toast { box-shadow: var(--shadow-3); }
/* ====== Empty state ====== */
.wn-empty {
border: 1px dashed var(--border-strong);
border-radius: var(--r-2);
padding: var(--sp-7);
display: flex; flex-direction: column; align-items: center;
gap: var(--sp-2);
text-align: center;
color: var(--fg-3);
}
.wn-empty__icon { color: var(--fg-3); margin-bottom: var(--sp-2); }
.wn-empty__title { font: 500 14px var(--ff-sans); color: var(--fg-2); margin: 0; }
.wn-empty__body { font: 400 13px/1.5 var(--ff-sans); color: var(--fg-3); max-width: 40ch; margin: 0; }
.wn-empty__cta { margin-top: var(--sp-2); }
/* ====== Top navigation ====== */
.wn-topnav {
height: 56px;
background: rgba(255, 255, 255, 0.92);
border-bottom: 1px solid var(--border);
display: flex; align-items: center;
gap: var(--sp-6);
padding: 0 var(--sp-5);
position: sticky; top: 0; z-index: 10;
}
.wn-topnav__brand { display: flex; align-items: center; gap: 10px; font: 500 14px var(--ff-sans); }
.wn-topnav__brand img { width: 22px; height: 22px; }
.wn-topnav__brand-slug { font-family: var(--ff-mono); font-size: 12px; color: var(--fg-3); letter-spacing: 0.04em; }
.wn-topnav__links { display: flex; gap: 22px; }
.wn-topnav__link {
font: 500 13px var(--ff-sans);
color: var(--fg-2);
text-decoration: none;
padding: 6px 0;
border-bottom: 1px solid transparent;
transition: color 120ms ease, border-color 120ms ease;
}
.wn-topnav__link:hover { color: var(--fg-1); }
.wn-topnav__link--active { color: var(--fg-1); border-bottom-color: var(--ink); }
.wn-topnav__right { margin-left: auto; display: flex; align-items: center; gap: var(--sp-3); }
/* ====== Sidebar ====== */
.wn-sidebar {
width: 240px;
flex: none;
background: var(--paper-2);
border-right: 1px solid var(--border);
padding: var(--sp-5) var(--sp-4);
display: flex; flex-direction: column; gap: var(--sp-5);
height: calc(100vh - 56px);
position: sticky; top: 56px;
overflow-y: auto;
}
.wn-sidebar__group { display: flex; flex-direction: column; gap: 8px; }
.wn-sidebar__group-label { padding-left: 12px; }
.wn-sidebar__item {
display: flex; align-items: center; gap: 10px;
padding: 8px 12px;
border-radius: 4px;
color: var(--fg-2);
font: 500 13px var(--ff-sans);
cursor: pointer; text-decoration: none;
transition: background 120ms ease, color 120ms ease;
}
.wn-sidebar__item:hover { color: var(--fg-1); }
.wn-sidebar__item--active {
color: var(--fg-1); background: var(--paper);
box-shadow: 0 0 0 1px var(--border) inset;
}
.wn-sidebar__item--doc { font-family: var(--ff-mono); font-size: 12px; }
.wn-sidebar__count { margin-left: auto; font: 400 11px var(--ff-mono); color: var(--fg-3); }
.wn-sidebar__footer { margin-top: auto; padding-top: var(--sp-3); border-top: 1px solid var(--border); }
.wn-sidebar__activation {
display: flex; align-items: center; gap: 8px; padding: 6px 12px;
font: 500 11px var(--ff-mono); letter-spacing: 0.06em; text-transform: uppercase;
color: var(--fg-2);
}
.wn-sidebar__activation-dot { width: 6px; height: 6px; border-radius: 999px; background: var(--hi-2); }
/* ====== Page header ====== */
.wn-page-header {
margin-bottom: var(--sp-6);
display: flex; flex-direction: column; gap: 8px;
}
.wn-page-header__row { display: flex; align-items: flex-end; gap: var(--sp-5); }
.wn-page-header__title {
font: 500 32px/1.15 var(--ff-sans);
letter-spacing: -0.015em;
margin: 0; flex: 1; color: var(--fg-1);
}
.wn-page-header__actions { display: flex; gap: 8px; flex-wrap: wrap; }
.wn-page-header__lede {
font: 400 16px/1.55 var(--ff-sans);
color: var(--fg-2);
margin: 0;
max-width: 60ch;
}
/* ====== Pipeline ====== */
.wn-pipeline {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0;
position: relative;
margin: 0 0 var(--sp-6);
}
.wn-pipeline__stage {
padding: 10px 12px 14px;
border-top: 2px solid var(--border);
display: flex; flex-direction: column; gap: 4px;
position: relative;
}
.wn-pipeline__stage--done { border-top-color: var(--ink); }
.wn-pipeline__stage--active { border-top-color: var(--hi-2); }
.wn-pipeline__num {
font: 500 10px/1 var(--ff-mono); letter-spacing: 0.1em; text-transform: uppercase;
color: var(--fg-3);
}
.wn-pipeline__stage--done .wn-pipeline__num,
.wn-pipeline__stage--active .wn-pipeline__num { color: var(--fg-1); }
.wn-pipeline__name { font: 500 14px/1.25 var(--ff-sans); color: var(--fg-1); }
.wn-pipeline__stage--pending .wn-pipeline__name { color: var(--fg-3); }
.wn-pipeline__meta { font: 400 11px/1.35 var(--ff-mono); color: var(--fg-3); }
.wn-pipeline__arrow {
position: absolute; top: -8px; right: -7px;
font: 400 14px var(--ff-mono); color: var(--ink-5);
}
.wn-pipeline__stage--done .wn-pipeline__arrow,
.wn-pipeline__stage--active .wn-pipeline__arrow { color: var(--ink); }
/* ====== Prototype card (combined card variant) ====== */
.wn-prototype-card { /* extends .wn-card */ }
.wn-prototype-card__qrow {
display: grid; grid-template-columns: 110px 1fr; gap: 6px 12px;
font-size: 13px; color: var(--fg-1);
}
.wn-prototype-card__qkey {
font: 500 11px/1.5 var(--ff-mono);
letter-spacing: 0.06em; text-transform: uppercase;
color: var(--fg-3);
}
.wn-prototype-card__qval { line-height: 1.45; }
/* ====== Layout helpers ====== */
.wn-main { padding: 40px 48px 80px; max-width: 1180px; }
.wn-app { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }