First commit
This commit is contained in:
211
frontend/app/assets/css/_mixins.scss
Normal file
211
frontend/app/assets/css/_mixins.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user