First commit

This commit is contained in:
2026-09-30 01:49:35 +02:00
commit 6edd03aace
109 changed files with 27020 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
// Shared shapes the rest of the design is cut from. Colours stay CSS custom
// properties throughout — [data-theme]/[data-accent] swap them at runtime.
// the dark backdrop a popup opens onto ($pad-y: how far down the sheet sits)
@mixin overlay($z, $pad-y: clamp(56px, 13vh, 132px)) {
position: fixed;
inset: 0;
z-index: $z;
display: flex;
justify-content: center;
align-items: flex-start;
padding: $pad-y 16px 16px;
background: color-mix(in srgb, #05070d 55%, transparent);
backdrop-filter: blur(3px);
}
// the paper dropped under a backdrop
@mixin sheet($w, $max-h: null) {
display: flex;
flex-direction: column;
width: min($w, 100%);
@if $max-h {
max-height: $max-h;
}
border: 1px solid var(--border);
border-radius: 16px;
background: var(--bg-elev);
box-shadow: var(--shadow-lg);
overflow: hidden;
}
// a popup's title row: the title, and the × at the end of it
@mixin sheet-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 13px 14px 13px 18px;
border-bottom: 1px solid var(--border);
}
@mixin sheet-title {
margin: 0;
font-size: 1.02rem;
font-weight: 700;
}
// the scrolling middle of a popup
@mixin sheet-body($pad: 18px) {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: $pad;
scrollbar-width: thin;
}
// the opening animation every popup shares: the backdrop fades, the sheet
// slides in from the edge it hangs nearest ($offset is where it starts)
@mixin popup-anim($name, $sheet, $dur, $sheet-move, $offset) {
.#{$name}-enter-active,
.#{$name}-leave-active {
transition: opacity $dur ease;
#{$sheet} { transition: $sheet-move; }
}
.#{$name}-enter-from,
.#{$name}-leave-to {
opacity: 0;
#{$sheet} { transform: translateY($offset) scale(0.98); }
}
}
// a menu that drops from a button: the popups' paper, on a hinge
// ($overflow: `auto` once it caps at a max-height; `visible` may not clip
// a nested dropdown)
@mixin dropdown-panel($z: 20, $overflow: auto) {
position: absolute;
z-index: $z;
top: calc(100% + 6px);
padding: 6px;
overflow-y: $overflow;
border-radius: 12px;
border: 1px solid var(--border);
background: var(--bg-elev);
box-shadow: var(--shadow-lg);
}
// a row inside a dropdown: full width, icon then label, an accent wash on hover
@mixin menu-row($pad, $gap, $fs: 0.85rem) {
display: flex;
align-items: center;
gap: $gap;
width: 100%;
padding: $pad;
border-radius: 8px;
color: var(--text);
font-size: $fs;
font-weight: 600;
transition: background-color 0.12s;
}
// the small caps label over a section of the interface
@mixin eyebrow($fs: 0.72rem, $fw: 750, $ls: 0.1em, $color: var(--faint)) {
font-size: $fs;
font-weight: $fw;
text-transform: uppercase;
letter-spacing: $ls;
color: $color;
}
// the account's face in a circle: an initial, or a picture laid over it
@mixin avatar($size, $fs) {
display: grid;
place-items: center;
flex-shrink: 0;
width: $size;
height: $size;
border-radius: 50%;
background: color-mix(in srgb, var(--accent) 14%, transparent);
color: var(--accent);
font-size: $fs;
font-weight: 800;
line-height: 1;
overflow: hidden;
img {
display: block;
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
}
}
// a square target that centres its icon
@mixin icon-target($size, $radius) {
display: grid;
place-items: center;
flex-shrink: 0;
width: $size;
height: $size;
border-radius: $radius;
}
// a quiet row that warms on hover (nav links, icon buttons)
@mixin hover-row {
transition: color 0.15s, background-color 0.15s;
&:hover { color: var(--text); background: var(--surface-2); }
}
// a row of choices styled as one segmented control
@mixin segmented {
display: inline-flex;
gap: 4px;
padding: 4px;
border: 1px solid var(--border);
border-radius: 12px;
background: var(--surface-2);
}
@mixin segmented-tab($pad, $fs) {
padding: $pad;
border-radius: 9px;
background: transparent;
color: var(--muted);
font-size: $fs;
font-weight: 600;
transition: color 0.15s, background-color 0.15s, box-shadow 0.15s;
&:hover { color: var(--text); }
&.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }
}
// the loading shimmer a skeleton wears
@mixin shimmer {
background: linear-gradient(
100deg,
var(--surface) 40%,
color-mix(in srgb, var(--surface) 55%, var(--text) 9%) 50%,
var(--surface) 60%
);
background-size: 200% 100%;
animation: shimmer 1.4s infinite linear;
}
// paint text with a gradient between the two accents
@mixin gradient-text($angle: 90deg) {
background: linear-gradient($angle, var(--accent), var(--accent-2));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
// a row of the book: no gutter along its edge (a book has none)
@mixin no-scrollbar {
scrollbar-width: none;
&::-webkit-scrollbar { display: none; }
}
// the × that closes a page or a chip: faint, and turns danger on hover
@mixin close-target {
&:hover {
color: var(--danger);
background: color-mix(in srgb, var(--danger) 10%, transparent);
}
}