874 lines
34 KiB
SCSS
874 lines
34 KiB
SCSS
|
|
@import 'variables';
|
||
|
|
@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap');
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Reset + Base */
|
||
|
|
/* ========================================================================== */
|
||
|
|
*,
|
||
|
|
*::before,
|
||
|
|
*::after { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
|
|
||
|
|
html, body { height: 100%; }
|
||
|
|
|
||
|
|
body {
|
||
|
|
font-family: $font-family-base;
|
||
|
|
font-size: map-get($font-sizes, base);
|
||
|
|
line-height: map-get($line-heights, normal);
|
||
|
|
color: map-get($colors, dark);
|
||
|
|
background: map-get($colors, white);
|
||
|
|
text-rendering: optimizeLegibility;
|
||
|
|
-webkit-font-smoothing: antialiased;
|
||
|
|
}
|
||
|
|
|
||
|
|
a { color: inherit; text-decoration: none; }
|
||
|
|
img, svg, video, canvas { display: block; max-width: 100%; height: auto; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Typography Utilities */
|
||
|
|
/* ========================================================================== */
|
||
|
|
@each $k, $v in $font-sizes { .text-#{$k} { font-size: $v; } }
|
||
|
|
@each $k, $v in $font-weights { .fw-#{$k} { font-weight: $v; } }
|
||
|
|
@each $k, $v in $line-heights { .lh-#{$k} { line-height: $v; } }
|
||
|
|
|
||
|
|
.text-left { text-align: left; }
|
||
|
|
.text-center { text-align: center; }
|
||
|
|
.text-right { text-align: right; }
|
||
|
|
.text-uppercase { text-transform: uppercase; }
|
||
|
|
.text-lowercase { text-transform: lowercase; }
|
||
|
|
.text-capitalize { text-transform: capitalize; }
|
||
|
|
.text-underline { text-decoration: underline; }
|
||
|
|
.text-line-through { text-decoration: line-through; }
|
||
|
|
.text-no-decoration { text-decoration: none; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Color Utilities */
|
||
|
|
/* ========================================================================== */
|
||
|
|
@each $name, $color in $colors {
|
||
|
|
.text-#{$name} { color: $color !important; }
|
||
|
|
.bg-#{$name} { background-color: $color !important; }
|
||
|
|
.border-#{$name} { border-color: $color !important; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Spacing Utilities (Margin / Padding) */
|
||
|
|
/* ========================================================================== */
|
||
|
|
@each $k, $v in $space {
|
||
|
|
.m-#{$k} { margin: $v !important; }
|
||
|
|
.mt-#{$k} { margin-top: $v !important; }
|
||
|
|
.mr-#{$k} { margin-right: $v !important; }
|
||
|
|
.mb-#{$k} { margin-bottom: $v !important; }
|
||
|
|
.ml-#{$k} { margin-left: $v !important; }
|
||
|
|
.mx-#{$k} { margin-left: $v !important; margin-right: $v !important; }
|
||
|
|
.my-#{$k} { margin-top: $v !important; margin-bottom: $v !important; }
|
||
|
|
|
||
|
|
.p-#{$k} { padding: $v !important; }
|
||
|
|
.pt-#{$k} { padding-top: $v !important; }
|
||
|
|
.pr-#{$k} { padding-right: $v !important; }
|
||
|
|
.pb-#{$k} { padding-bottom: $v !important; }
|
||
|
|
.pl-#{$k} { padding-left: $v !important; }
|
||
|
|
.px-#{$k} { padding-left: $v !important; padding-right: $v !important; }
|
||
|
|
.py-#{$k} { padding-top: $v !important; padding-bottom: $v !important; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Responsive spacing */
|
||
|
|
@each $bp, $w in $breakpoints {
|
||
|
|
@media (min-width: $w) {
|
||
|
|
@each $k, $v in $space {
|
||
|
|
.#{$bp}-m-#{$k} { margin: $v !important; }
|
||
|
|
.#{$bp}-mt-#{$k} { margin-top: $v !important; }
|
||
|
|
.#{$bp}-mr-#{$k} { margin-right: $v !important; }
|
||
|
|
.#{$bp}-mb-#{$k} { margin-bottom: $v !important; }
|
||
|
|
.#{$bp}-ml-#{$k} { margin-left: $v !important; }
|
||
|
|
.#{$bp}-mx-#{$k} { margin-left: $v !important; margin-right: $v !important; }
|
||
|
|
.#{$bp}-my-#{$k} { margin-top: $v !important; margin-bottom: $v !important; }
|
||
|
|
|
||
|
|
.#{$bp}-p-#{$k} { padding: $v !important; }
|
||
|
|
.#{$bp}-pt-#{$k} { padding-top: $v !important; }
|
||
|
|
.#{$bp}-pr-#{$k} { padding-right: $v !important; }
|
||
|
|
.#{$bp}-pb-#{$k} { padding-bottom: $v !important; }
|
||
|
|
.#{$bp}-pl-#{$k} { padding-left: $v !important; }
|
||
|
|
.#{$bp}-px-#{$k} { padding-left: $v !important; padding-right: $v !important; }
|
||
|
|
.#{$bp}-py-#{$k} { padding-top: $v !important; padding-bottom: $v !important; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Sizing Utilities (Width / Height) */
|
||
|
|
/* ========================================================================== */
|
||
|
|
@each $k, $v in $size-percent { .w-#{$k} { width: $v; } .h-#{$k} { height: $v; } }
|
||
|
|
@each $k, $v in $size-px { .wpx-#{$k} { width: $v; } .hpx-#{$k} { height: $v; } }
|
||
|
|
@each $k, $v in $size-rem { .wrem-#{$k} { width: $v; } .hrem-#{$k} { height: $v; } }
|
||
|
|
@each $k, $v in $size-vw { .wvw-#{$k} { width: $v; } }
|
||
|
|
@each $k, $v in $size-vh { .hvh-#{$k} { height: $v; } }
|
||
|
|
@each $k, $v in $size-dvh { .hdvh-#{$k} { height: $v; } }
|
||
|
|
@each $k, $v in $size-svh { .hsvh-#{$k} { height: $v; } }
|
||
|
|
@each $k, $v in $size-lvh { .hlvh-#{$k} { height: $v; } }
|
||
|
|
|
||
|
|
/* Responsive sizing */
|
||
|
|
@each $bp, $w in $breakpoints {
|
||
|
|
@media (min-width: $w) {
|
||
|
|
@each $k, $v in $size-percent { .#{$bp}-w-#{$k} { width: $v; } .#{$bp}-h-#{$k} { height: $v; } }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Display + Layout */
|
||
|
|
/* ========================================================================== */
|
||
|
|
@each $t in $display-types { .d-#{$t} { display: $t !important; } }
|
||
|
|
|
||
|
|
.container {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
@each $bp, $mw in $container-max-widths {
|
||
|
|
@media (min-width: map-get($breakpoints, $bp)) { max-width: $mw; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Container variants */
|
||
|
|
.container-fluid {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-bleed {
|
||
|
|
width: 100vw;
|
||
|
|
margin-left: 50%;
|
||
|
|
transform: translateX(-50%);
|
||
|
|
padding-inline: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-narrow {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: clamp(36rem, 70vw, 56rem);
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-wide {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: clamp(72rem, 92vw, 100rem);
|
||
|
|
}
|
||
|
|
.container-fluid {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-bleed {
|
||
|
|
width: 100vw;
|
||
|
|
margin-left: 50%;
|
||
|
|
transform: translateX(-50%);
|
||
|
|
padding-inline: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-narrow {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: clamp(36rem, 70vw, 56rem);
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-wide {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: clamp(72rem, 92vw, 100rem);
|
||
|
|
}
|
||
|
|
.container-fluid {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-bleed {
|
||
|
|
width: 100vw;
|
||
|
|
margin-left: 50%;
|
||
|
|
transform: translateX(-50%);
|
||
|
|
padding-inline: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-narrow {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: clamp(36rem, 70vw, 56rem);
|
||
|
|
}
|
||
|
|
|
||
|
|
.container-wide {
|
||
|
|
width: 100%;
|
||
|
|
margin-inline: auto;
|
||
|
|
padding-inline: $container-padding;
|
||
|
|
max-width: clamp(72rem, 92vw, 100rem);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Flex grid */
|
||
|
|
.row {
|
||
|
|
display: flex;
|
||
|
|
flex-wrap: wrap;
|
||
|
|
margin-inline: map-get($gutters, 3);
|
||
|
|
margin-block: map-get($gutters, 3);
|
||
|
|
}
|
||
|
|
.col { flex: 1 0 0%; padding-inline: map-get($gutters, 3); padding-block: map-get($gutters, 3); }
|
||
|
|
.col-auto { flex: 0 0 auto; width: auto; padding-inline: map-get($gutters, 3); padding-block: map-get($gutters, 3); }
|
||
|
|
|
||
|
|
@for $i from 1 through $grid-columns {
|
||
|
|
.col-#{$i} {
|
||
|
|
flex: 0 0 percentage($i / $grid-columns);
|
||
|
|
max-width: percentage($i / $grid-columns);
|
||
|
|
padding-inline: map-get($gutters, 3);
|
||
|
|
padding-block: map-get($gutters, 3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
@each $bp, $w in $breakpoints {
|
||
|
|
@media (min-width: $w) {
|
||
|
|
.col-#{$bp} { flex: 1 0 0%; padding-inline: map-get($gutters, 3); padding-block: map-get($gutters, 3); }
|
||
|
|
.col-#{$bp}-auto { flex: 0 0 auto; width: auto; padding-inline: map-get($gutters, 3); padding-block: map-get($gutters, 3); }
|
||
|
|
@for $i from 1 through $grid-columns {
|
||
|
|
.col-#{$bp}-#{$i} {
|
||
|
|
flex: 0 0 percentage($i / $grid-columns);
|
||
|
|
max-width: percentage($i / $grid-columns);
|
||
|
|
padding-inline: map-get($gutters, 3);
|
||
|
|
padding-block: map-get($gutters, 3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Flex helpers */
|
||
|
|
.flex { display: flex; }
|
||
|
|
.inline-flex { display: inline-flex; }
|
||
|
|
@each $k, $v in $flex-directions { .flex-#{$k} { display: flex; flex-direction: $v; } }
|
||
|
|
@each $k, $v in $justify-content-values { .justify-#{$k} { display: flex; justify-content: $v; } }
|
||
|
|
@each $k, $v in $align-items-values { .items-#{$k} { display: flex; align-items: $v; } }
|
||
|
|
|
||
|
|
/* Borders + Radius + Shadows */
|
||
|
|
@each $k, $v in $border-widths { .border-#{$k} { border-width: $v; border-style: solid; border-color: currentColor; } }
|
||
|
|
.border { border: 1px solid currentColor; }
|
||
|
|
.border-top { border-top: 1px solid currentColor; }
|
||
|
|
.border-right { border-right: 1px solid currentColor; }
|
||
|
|
.border-bottom { border-bottom: 1px solid currentColor; }
|
||
|
|
.border-left { border-left: 1px solid currentColor; }
|
||
|
|
|
||
|
|
@each $k, $v in $radii { .rounded-#{$k} { border-radius: $v; } }
|
||
|
|
.rounded { border-radius: map-get($radii, md); }
|
||
|
|
.rounded-top { border-top-left-radius: map-get($radii, md); border-top-right-radius: map-get($radii, md); }
|
||
|
|
.rounded-bottom { border-bottom-left-radius: map-get($radii, md); border-bottom-right-radius: map-get($radii, md); }
|
||
|
|
.rounded-left { border-top-left-radius: map-get($radii, md); border-bottom-left-radius: map-get($radii, md); }
|
||
|
|
.rounded-right { border-top-right-radius: map-get($radii, md); border-bottom-right-radius: map-get($radii, md); }
|
||
|
|
|
||
|
|
@each $k, $v in $shadows { .shadow-#{$k} { box-shadow: $v !important; } }
|
||
|
|
@each $k, $v in $drop-shadows { .drop-shadow-#{$k} { box-shadow: $v !important; } }
|
||
|
|
|
||
|
|
/* Z-Index */
|
||
|
|
.z-base { z-index: map-get($z, base); }
|
||
|
|
.z-dropdown { z-index: map-get($z, dropdown); }
|
||
|
|
.z-overlay { z-index: map-get($z, overlay); }
|
||
|
|
.z-modal { z-index: map-get($z, modal); }
|
||
|
|
.z-toast { z-index: map-get($z, toast); }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Grid System (CSS Grid Utilities) */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.grid { display: grid !important; }
|
||
|
|
.inline-grid { display: inline-grid !important; }
|
||
|
|
|
||
|
|
@for $i from 1 through 12 { .grid-cols-#{$i} { grid-template-columns: repeat($i, 1fr) !important; } }
|
||
|
|
.grid-cols-none { grid-template-columns: none !important; }
|
||
|
|
|
||
|
|
@each $bp, $width in $breakpoints {
|
||
|
|
@media (min-width: $width) {
|
||
|
|
@for $i from 1 through 12 {
|
||
|
|
.#{$bp}-grid-cols-#{$i} { grid-template-columns: repeat($i, 1fr) !important; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@for $i from 1 through 12 { .grid-rows-#{$i} { grid-template-rows: repeat($i, 1fr) !important; } }
|
||
|
|
.grid-rows-none { grid-template-rows: none !important; }
|
||
|
|
|
||
|
|
.grid-auto-fit-sm { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fit-md { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fit-lg { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fill-sm { grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fill-md { grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fill-lg { grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)) !important; }
|
||
|
|
|
||
|
|
.grid-align-center { align-items: center !important; justify-items: center !important; }
|
||
|
|
.grid-align-start { align-items: start !important; justify-items: start !important; }
|
||
|
|
.grid-align-end { align-items: end !important; justify-items: end !important; }
|
||
|
|
.grid-align-stretch { align-items: stretch !important; justify-items: stretch !important; }
|
||
|
|
|
||
|
|
.place-self-center { place-self: center !important; }
|
||
|
|
.place-self-start { place-self: start !important; }
|
||
|
|
.place-self-end { place-self: end !important; }
|
||
|
|
.place-self-stretch { place-self: stretch !important; }
|
||
|
|
|
||
|
|
.grid-flow-row { grid-auto-flow: row !important; }
|
||
|
|
.grid-flow-col { grid-auto-flow: column !important; }
|
||
|
|
.grid-flow-row-dense { grid-auto-flow: row dense !important; }
|
||
|
|
.grid-flow-col-dense { grid-auto-flow: column dense !important; }
|
||
|
|
|
||
|
|
/* Auto rows/cols sizing */
|
||
|
|
.grid-auto-cols-min { grid-auto-columns: min-content !important; }
|
||
|
|
.grid-auto-cols-max { grid-auto-columns: max-content !important; }
|
||
|
|
.grid-auto-cols-fr { grid-auto-columns: 1fr !important; }
|
||
|
|
.grid-auto-rows-min { grid-auto-rows: min-content !important; }
|
||
|
|
.grid-auto-rows-max { grid-auto-rows: max-content !important; }
|
||
|
|
.grid-auto-rows-fr { grid-auto-rows: 1fr !important; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Control Blueprints (shared by inputs/buttons/etc.) */
|
||
|
|
/* ========================================================================== */
|
||
|
|
@mixin control-base {
|
||
|
|
appearance: none;
|
||
|
|
border-style: map-get($control-border, style);
|
||
|
|
border-width: map-get($control-border, width);
|
||
|
|
border-color: map-get($control-colors, border);
|
||
|
|
background-color: map-get($control-colors, bg);
|
||
|
|
color: map-get($control-colors, fg);
|
||
|
|
outline: 0;
|
||
|
|
transition:
|
||
|
|
background-color map-get($timings, base) map-get($easings, in-out),
|
||
|
|
color map-get($timings, base) map-get($easings, in-out),
|
||
|
|
border-color map-get($timings, base) map-get($easings, in-out),
|
||
|
|
box-shadow map-get($timings, base) map-get($easings, in-out),
|
||
|
|
transform map-get($timings, fast) map-get($easings, out);
|
||
|
|
}
|
||
|
|
|
||
|
|
@mixin control-size($size) {
|
||
|
|
$props: map-get($control-sizes, $size);
|
||
|
|
font-size: map-get($props, font-size);
|
||
|
|
line-height: map-get($props, line-height);
|
||
|
|
padding: map-get($props, py) map-get($props, px);
|
||
|
|
border-radius: map-get($props, radius);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Inputs */
|
||
|
|
.input, select, textarea {
|
||
|
|
@include control-base;
|
||
|
|
width: 100%;
|
||
|
|
&:focus {
|
||
|
|
border-color: map-get($control-colors, focus);
|
||
|
|
box-shadow: 0 0 0 3px map-get($control-colors, ring);
|
||
|
|
background-color: map-get($colors, white);
|
||
|
|
}
|
||
|
|
&:disabled {
|
||
|
|
background-color: map-get($control-colors, disabled-bg);
|
||
|
|
opacity: .6;
|
||
|
|
cursor: not-allowed;
|
||
|
|
}
|
||
|
|
&::placeholder { color: map-get($control-colors, placeholder); }
|
||
|
|
}
|
||
|
|
@each $size, $props in $control-sizes { .input-#{$size} { @include control-size($size); } }
|
||
|
|
|
||
|
|
/* Buttons */
|
||
|
|
.btn {
|
||
|
|
@include control-base;
|
||
|
|
display: inline-flex;
|
||
|
|
width: auto;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
cursor: pointer;
|
||
|
|
user-select: none;
|
||
|
|
box-shadow: map-get($shadows, 1);
|
||
|
|
&:focus-visible {
|
||
|
|
border-color: map-get($control-colors, focus);
|
||
|
|
box-shadow: 0 0 0 3px map-get($control-colors, ring), map-get($shadows, 2);
|
||
|
|
}
|
||
|
|
&:active { transform: translateY(1px); box-shadow: map-get($shadows, 3); }
|
||
|
|
&:disabled { opacity: .6; cursor: not-allowed; pointer-events: none; }
|
||
|
|
}
|
||
|
|
@each $size, $props in $control-sizes { .btn-#{$size} { @include control-size($size); } }
|
||
|
|
|
||
|
|
@each $name, $color in $colors {
|
||
|
|
.btn-#{$name} {
|
||
|
|
background-color: $color;
|
||
|
|
color: if(lightness($color) > 60%, map-get($colors, dark), map-get($colors, white));
|
||
|
|
border-color: transparent;
|
||
|
|
&:hover { background-color: darken($color, 8%); box-shadow: map-get($shadows, 2); }
|
||
|
|
}
|
||
|
|
.btn-outline-#{$name} {
|
||
|
|
background-color: transparent;
|
||
|
|
color: $color;
|
||
|
|
border-color: $color;
|
||
|
|
&:hover { background-color: $color; color: if(lightness($color) > 60%, map-get($colors, dark), map-get($colors, white)); }
|
||
|
|
}
|
||
|
|
.btn-ghost-#{$name} {
|
||
|
|
background-color: transparent;
|
||
|
|
color: $color;
|
||
|
|
border-color: transparent;
|
||
|
|
&:hover { background-color: rgba($color, .12); }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Alerts */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.alert {
|
||
|
|
padding: map-get($space, 2) map-get($space, 3);
|
||
|
|
border-radius: map-get($radii, md);
|
||
|
|
border: 1px solid map-get($control-colors, border);
|
||
|
|
background: map-get($colors, accent-white);
|
||
|
|
}
|
||
|
|
@each $name, $color in $colors {
|
||
|
|
.alert-#{$name} {
|
||
|
|
background: mix($color, map-get($colors, white), 12%);
|
||
|
|
border-color: $color;
|
||
|
|
color: if(lightness($color) > 60%, map-get($colors, dark), map-get($colors, dark));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Cards (with custom borders and shadows) */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.card {
|
||
|
|
background: map-get($colors, white);
|
||
|
|
border: 1px solid map-get($control-colors, border);
|
||
|
|
border-radius: map-get($radii, lg);
|
||
|
|
box-shadow: map-get($shadows, 1);
|
||
|
|
transition: box-shadow map-get($timings, base) map-get($easings, in-out);
|
||
|
|
}
|
||
|
|
.card-hover:hover { box-shadow: map-get($shadows, 3); }
|
||
|
|
|
||
|
|
@each $k, $v in $shadows { .card-shadow-#{$k} { box-shadow: $v; } }
|
||
|
|
@each $name, $color in $colors {
|
||
|
|
.card-border-#{$name} { border-color: $color; }
|
||
|
|
.card-bg-#{$name} { background-color: $color; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Modals */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.modal {
|
||
|
|
position: fixed;
|
||
|
|
inset: 0;
|
||
|
|
display: none;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
padding: map-get($space, 3);
|
||
|
|
z-index: map-get($z, modal);
|
||
|
|
}
|
||
|
|
.modal.open { display: flex; }
|
||
|
|
|
||
|
|
.modal__overlay { position: absolute; inset: 0; background: rgba(map-get($colors, black), .5); }
|
||
|
|
|
||
|
|
.modal__dialog {
|
||
|
|
position: relative;
|
||
|
|
width: min(720px, 100%);
|
||
|
|
background: map-get($colors, white);
|
||
|
|
border: 1px solid map-get($control-colors, border);
|
||
|
|
border-radius: map-get($radii, lg);
|
||
|
|
box-shadow: map-get($shadows, 5);
|
||
|
|
padding: map-get($space, 3);
|
||
|
|
}
|
||
|
|
|
||
|
|
.modal__header { display: flex; align-items: center; justify-content: space-between; gap: map-get($space, 2); margin-bottom: map-get($space, 2); }
|
||
|
|
.modal__title { font-size: map-get($font-sizes, lg); font-weight: map-get($font-weights, semibold); }
|
||
|
|
.modal__close { @extend .btn; @extend .btn-sm; background: transparent; box-shadow: none; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Pills */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.pill {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: map-get($space, 1);
|
||
|
|
padding: map-get($space, 1) map-get($space, 2);
|
||
|
|
border-radius: map-get($radii, full);
|
||
|
|
border: 1px solid map-get($control-colors, border);
|
||
|
|
background: map-get($colors, accent-white);
|
||
|
|
font-size: map-get($font-sizes, sm);
|
||
|
|
line-height: map-get($line-heights, normal);
|
||
|
|
}
|
||
|
|
@each $name, $color in $colors {
|
||
|
|
.pill-#{$name} {
|
||
|
|
background: mix($color, map-get($colors, white), 12%);
|
||
|
|
color: if(lightness($color) > 60%, map-get($colors, dark), map-get($colors, white));
|
||
|
|
border-color: $color;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Accordions */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.accordion { border: 1px solid map-get($control-colors, border); border-radius: map-get($radii, lg); overflow: hidden; }
|
||
|
|
.accordion__item + .accordion__item { border-top: 1px solid map-get($control-colors, border); }
|
||
|
|
|
||
|
|
.accordion__header {
|
||
|
|
width: 100%;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
gap: map-get($space, 2);
|
||
|
|
padding: map-get($space, 2) map-get($space, 3);
|
||
|
|
cursor: pointer;
|
||
|
|
background: map-get($colors, accent-white);
|
||
|
|
border: none;
|
||
|
|
}
|
||
|
|
.accordion__title { font-weight: map-get($font-weights, medium); }
|
||
|
|
.accordion__icon { transition: transform map-get($timings, base) map-get($easings, in-out); }
|
||
|
|
|
||
|
|
.accordion__panel {
|
||
|
|
display: grid;
|
||
|
|
grid-template-rows: 0fr;
|
||
|
|
transition: grid-template-rows map-get($timings, base) map-get($easings, in-out);
|
||
|
|
}
|
||
|
|
.accordion__content { overflow: hidden; padding: 0 map-get($space, 3); }
|
||
|
|
|
||
|
|
.accordion__item.is-open .accordion__panel { grid-template-rows: 1fr; }
|
||
|
|
.accordion__item.is-open .accordion__content { padding: map-get($space, 2) map-get($space, 3) map-get($space, 3); }
|
||
|
|
.accordion__item.is-open .accordion__icon { transform: rotate(180deg); }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Stack Utilities */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.stack > * + * { margin-top: map-get($space, 3); }
|
||
|
|
@each $k, $v in $space { .stack-#{$k} > * + * { margin-top: $v; } }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Transitions & Animations */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.transition-all { transition: all map-get($timings, base) map-get($easings, in-out); }
|
||
|
|
.transition-fast { transition-duration: map-get($timings, fast); }
|
||
|
|
.transition-slow { transition-duration: map-get($timings, slow); }
|
||
|
|
|
||
|
|
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
||
|
|
@keyframes slide-up { from { transform: translateY(10px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
||
|
|
|
||
|
|
.animate-fade-in { animation: fade-in .3s map-get($easings, out) both; }
|
||
|
|
.animate-slide-up { animation: slide-up .35s map-get($easings, out) both; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Tooltips */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.tooltip { position: relative; display: inline-block; cursor: help; }
|
||
|
|
.tooltip::after {
|
||
|
|
content: attr(data-tip);
|
||
|
|
position: absolute;
|
||
|
|
bottom: calc(100% + 6px);
|
||
|
|
left: 50%;
|
||
|
|
transform: translateX(-50%);
|
||
|
|
background: map-get($colors, dark);
|
||
|
|
color: map-get($colors, white);
|
||
|
|
padding: .25rem .5rem;
|
||
|
|
font-size: map-get($font-sizes, sm);
|
||
|
|
border-radius: map-get($radii, sm);
|
||
|
|
white-space: nowrap;
|
||
|
|
opacity: 0;
|
||
|
|
pointer-events: none;
|
||
|
|
transition: opacity map-get($timings, base) map-get($easings, in-out);
|
||
|
|
z-index: map-get($z, dropdown);
|
||
|
|
}
|
||
|
|
.tooltip:hover::after { opacity: 1; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Tabs */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.tabs { display: flex; gap: map-get($space, 2); border-bottom: 1px solid map-get($control-colors, border); }
|
||
|
|
.tab {
|
||
|
|
padding: map-get($space, 2) map-get($space, 3);
|
||
|
|
cursor: pointer;
|
||
|
|
border-bottom: 2px solid transparent;
|
||
|
|
transition:
|
||
|
|
border-color map-get($timings, base) map-get($easings, in-out),
|
||
|
|
color map-get($timings, base) map-get($easings, in-out);
|
||
|
|
}
|
||
|
|
.tab.active { border-color: map-get($colors, primary); color: map-get($colors, primary); }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Transform & Scale Utilities */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.scale-90 { transform: scale(.9); }
|
||
|
|
.scale-95 { transform: scale(.95); }
|
||
|
|
.scale-100 { transform: scale(1); }
|
||
|
|
.scale-105 { transform: scale(1.05); }
|
||
|
|
.scale-110 { transform: scale(1.1); }
|
||
|
|
|
||
|
|
.rotate-90 { transform: rotate(90deg); }
|
||
|
|
.rotate-180 { transform: rotate(180deg); }
|
||
|
|
.rotate-270 { transform: rotate(270deg); }
|
||
|
|
|
||
|
|
.skew-x-6 { transform: skewX(6deg); }
|
||
|
|
.skew-y-6 { transform: skewY(6deg); }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Shadow, Blur, and Glow Utilities */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.shadow-none { box-shadow: none !important; }
|
||
|
|
.blur-sm { filter: blur(2px); }
|
||
|
|
.blur-md { filter: blur(4px); }
|
||
|
|
.blur-lg { filter: blur(8px); }
|
||
|
|
.glow { box-shadow: 0 0 12px rgba(map-get($colors, accent), .5); }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Dark Mode (opt-in via .theme-dark) */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.theme-dark { background: map-get($colors, dark); color: map-get($colors, light); }
|
||
|
|
.theme-dark .card,
|
||
|
|
.theme-dark .modal__dialog {
|
||
|
|
background: map-get($colors, secondary);
|
||
|
|
border-color: map-get($colors, muted);
|
||
|
|
color: map-get($colors, light);
|
||
|
|
}
|
||
|
|
.theme-dark .input,
|
||
|
|
.theme-dark select,
|
||
|
|
.theme-dark textarea {
|
||
|
|
background: map-get($colors, secondary);
|
||
|
|
color: map-get($colors, light);
|
||
|
|
border-color: map-get($colors, muted);
|
||
|
|
}
|
||
|
|
.theme-dark .tabs { border-bottom-color: map-get($colors, muted); }
|
||
|
|
.theme-dark .tab.active { border-color: map-get($colors, primary); color: map-get($colors, primary); }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Scroll Utilities */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.scroll-y { overflow-y: auto !important; }
|
||
|
|
.scroll-x { overflow-x: auto !important; }
|
||
|
|
.scroll-hidden { overflow: hidden !important; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Spinner */
|
||
|
|
/* ========================================================================== */
|
||
|
|
@keyframes ui-spinner { to { transform: rotate(360deg); } }
|
||
|
|
|
||
|
|
.spinner {
|
||
|
|
display: inline-block;
|
||
|
|
width: 1rem;
|
||
|
|
height: 1rem;
|
||
|
|
border-radius: 50%;
|
||
|
|
border-style: solid;
|
||
|
|
border-width: 2px;
|
||
|
|
border-color: map-get($colors, dark);
|
||
|
|
border-left-color: transparent !important;
|
||
|
|
animation: ui-spinner 1s linear infinite;
|
||
|
|
}
|
||
|
|
@each $name, $color in $colors {
|
||
|
|
.spinner-#{$name} { border-color: $color; border-left-color: transparent !important; }
|
||
|
|
}
|
||
|
|
.spinner-sm { width: .875rem; height: .875rem; border-width: 2px; }
|
||
|
|
.spinner-md { width: 1.25rem; height: 1.25rem; border-width: 2px; }
|
||
|
|
.spinner-lg { width: 1.75rem; height: 1.75rem; border-width: 3px; }
|
||
|
|
.spinner-xl { width: 2.25rem; height: 2.25rem; border-width: 4px; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Visibility + Accessibility */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.visible { visibility: visible !important; }
|
||
|
|
.invisible { visibility: hidden !important; }
|
||
|
|
|
||
|
|
.sr-only {
|
||
|
|
position: absolute !important;
|
||
|
|
width: 1px !important;
|
||
|
|
height: 1px !important;
|
||
|
|
padding: 0 !important;
|
||
|
|
margin: -1px !important;
|
||
|
|
overflow: hidden !important;
|
||
|
|
clip: rect(0, 0, 0, 0) !important;
|
||
|
|
white-space: nowrap !important;
|
||
|
|
border: 0 !important;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Overflow + Text Truncation */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.overflow-hidden { overflow: hidden !important; }
|
||
|
|
.overflow-auto { overflow: auto !important; }
|
||
|
|
.overflow-scroll { overflow: scroll !important; }
|
||
|
|
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Opacity */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.opacity-0 { opacity: 0; }
|
||
|
|
.opacity-25 { opacity: .25; }
|
||
|
|
.opacity-50 { opacity: .5; }
|
||
|
|
.opacity-75 { opacity: .75; }
|
||
|
|
.opacity-100 { opacity: 1; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Cursor + Pointer Events */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.cursor-default { cursor: default !important; }
|
||
|
|
.cursor-pointer { cursor: pointer !important; }
|
||
|
|
.cursor-not-allowed { cursor: not-allowed !important; }
|
||
|
|
.pointer-none { pointer-events: none !important; }
|
||
|
|
.pointer-auto { pointer-events: auto !important; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Position + Inset */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.relative { position: relative !important; }
|
||
|
|
.absolute { position: absolute !important; }
|
||
|
|
.fixed { position: fixed !important; }
|
||
|
|
.sticky { position: sticky !important; }
|
||
|
|
|
||
|
|
.inset-0 { inset: 0 !important; }
|
||
|
|
.top-0 { top: 0 !important; }
|
||
|
|
.right-0 { right: 0 !important; }
|
||
|
|
.bottom-0 { bottom: 0 !important; }
|
||
|
|
.left-0 { left: 0 !important; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Flex Wrapping + Gap */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.wrap { flex-wrap: wrap !important; }
|
||
|
|
.nowrap { flex-wrap: nowrap !important; }
|
||
|
|
.wrap-reverse { flex-wrap: wrap-reverse !important; }
|
||
|
|
|
||
|
|
@each $k, $v in $space {
|
||
|
|
.gap-#{$k} { gap: $v !important; }
|
||
|
|
.row-gap-#{$k} { row-gap: $v !important; }
|
||
|
|
.col-gap-#{$k} { column-gap: $v !important; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Responsive Reverse (small-down) */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.sm-reverse {
|
||
|
|
@media (max-width: map-get($breakpoints, md)) {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column-reverse;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Aspect Ratios + Object Fit */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.ratio { position: relative; width: 100%; height: 0; overflow: hidden; }
|
||
|
|
.ratio > * { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||
|
|
|
||
|
|
.ratio-16x9 { padding-bottom: 56.25%; }
|
||
|
|
.ratio-4x3 { padding-bottom: 75%; }
|
||
|
|
.ratio-1x1 { padding-bottom: 100%; }
|
||
|
|
|
||
|
|
.object-contain { object-fit: contain !important; }
|
||
|
|
.object-cover { object-fit: cover !important; }
|
||
|
|
.object-fill { object-fit: fill !important; }
|
||
|
|
.object-none { object-fit: none !important; }
|
||
|
|
.object-scale-down { object-fit: scale-down !important; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Hover Motion Helpers */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.hover-raise { transition: transform map-get($timings, base) map-get($easings, in-out), box-shadow map-get($timings, base) map-get($easings, in-out); }
|
||
|
|
.hover-raise:hover { transform: translateY(-2px); box-shadow: map-get($shadows, 2); }
|
||
|
|
|
||
|
|
.hover-glow { transition: box-shadow map-get($timings, base) map-get($easings, in-out); }
|
||
|
|
.hover-glow:hover { box-shadow: 0 0 0 3px map-get($control-colors, ring), map-get($shadows, 2); }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Divider Utilities */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.hr { width: 100%; height: 1px; background: map-get($control-colors, border); border: 0; }
|
||
|
|
.hr-dashed { height: 1px; border-top: 1px dashed map-get($control-colors, border); background: transparent; }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Utility Badges (aliases of pills) */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.badge { @extend .pill; }
|
||
|
|
@each $name, $color in $colors { .badge-#{$name} { @extend .pill-#{$name}; } }
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Quick Utility Aliases */
|
||
|
|
/* ========================================================================== */
|
||
|
|
.rel { position: relative; }
|
||
|
|
.no-click { pointer-events: none; }
|
||
|
|
.center { display: grid; place-items: center; }
|
||
|
|
.full-height { height: 100vh; }
|
||
|
|
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* Flex Grid Extensions */
|
||
|
|
/* ========================================================================== */
|
||
|
|
|
||
|
|
.row.no-gutter {
|
||
|
|
margin-inline: 0;
|
||
|
|
margin-block: 0;
|
||
|
|
|
||
|
|
> .col,
|
||
|
|
> [class^="col-"] {
|
||
|
|
padding-inline: 0;
|
||
|
|
padding-block: 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.row.gx-0 {
|
||
|
|
margin-right: 0;
|
||
|
|
margin-left: 0;
|
||
|
|
|
||
|
|
> .col,
|
||
|
|
> [class^="col-"] {
|
||
|
|
padding-right: 0;
|
||
|
|
padding-left: 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.row.gy-0 {
|
||
|
|
margin-top: 0;
|
||
|
|
margin-bottom: 0;
|
||
|
|
|
||
|
|
> .col,
|
||
|
|
> [class^="col-"] {
|
||
|
|
padding-top: 0;
|
||
|
|
padding-bottom: 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.row-bleed {
|
||
|
|
margin-right: calc(#{$container-padding} * -1);
|
||
|
|
margin-left: calc(#{$container-padding} * -1);
|
||
|
|
}
|
||
|
|
|
||
|
|
@each $bp, $width in $breakpoints {
|
||
|
|
@media (min-width: $width) {
|
||
|
|
.#{$bp}-row-center { justify-content: center; }
|
||
|
|
.#{$bp}-row-between { justify-content: space-between; }
|
||
|
|
.#{$bp}-row-around { justify-content: space-around; }
|
||
|
|
.#{$bp}-row-evenly { justify-content: space-evenly; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/* ========================================================================== */
|
||
|
|
/* CSS Grid Enhancements */
|
||
|
|
/* ========================================================================== */
|
||
|
|
|
||
|
|
@for $i from 1 through 12 {
|
||
|
|
.grid-cols-#{$i} { grid-template-columns: repeat($i, 1fr) !important; }
|
||
|
|
}
|
||
|
|
|
||
|
|
.grid-cols-none { grid-template-columns: none !important; }
|
||
|
|
|
||
|
|
@for $i from 1 through 12 {
|
||
|
|
.grid-rows-#{$i} { grid-template-rows: repeat($i, 1fr) !important; }
|
||
|
|
}
|
||
|
|
|
||
|
|
.grid-rows-none { grid-template-rows: none !important; }
|
||
|
|
|
||
|
|
.grid-auto-fit-sm { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fit-md { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fit-lg { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)) !important; }
|
||
|
|
|
||
|
|
.grid-auto-fill-sm { grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fill-md { grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)) !important; }
|
||
|
|
.grid-auto-fill-lg { grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)) !important; }
|
||
|
|
|
||
|
|
.grid-align-center { align-items: center !important; justify-items: center !important; }
|
||
|
|
.grid-align-start { align-items: start !important; justify-items: start !important; }
|
||
|
|
.grid-align-end { align-items: end !important; justify-items: end !important; }
|
||
|
|
.grid-align-stretch { align-items: stretch !important; justify-items: stretch !important; }
|
||
|
|
|
||
|
|
.grid-flow-row { grid-auto-flow: row !important; }
|
||
|
|
.grid-flow-col { grid-auto-flow: column !important; }
|
||
|
|
.grid-flow-row-dense { grid-auto-flow: row dense !important; }
|
||
|
|
.grid-flow-col-dense { grid-auto-flow: column dense !important; }
|