Files
mathew/frontend/app/assets/css/_mixins.scss
2026-09-30 01:49:35 +02:00

212 lines
5.1 KiB
SCSS
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.
// 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);
}
}