@fylgja/base
Version:
Base provides a streamlined starting point for any web project. It’s a collection of class-less solutions, allowing you to focus on building your site.
630 lines (551 loc) • 16.1 kB
CSS
/**
* Fylgja (https://fylgja.dev)
* Licensed under MIT Open Source
*/
*,
::before,
::after {
box-sizing: border-box;
border: 0 solid;
}
:root {
--root-bg: hsl(0, 0%, 100%);
--root-fg: hsl(0, 0%, 8%);
--family-system: system-ui, sans-serif;
--family-mono: ui-monospace, Menlo, Consolas, monospace;
--select-light-scheme: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="hsl(0 0% 0%)"><path d="m6 9 6 6 6-6"/></svg>');
--select-dark-scheme: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="hsl(0 0% 100%)"><path d="m6 9 6 6 6-6"/></svg>');
--outline-transition: outline-offset 150ms cubic-bezier(0.25, 0, 0.4, 1);
--surface-color: color-mix(in srgb, var(--root-bg) 94%, var(--root-fg));
}
:where(:focus-visible) {
outline: var(--outline-size, 2px) solid;
outline-offset: var(--outline-offset, 2px);
}
:where(:active:focus-visible) {
outline-offset: 0;
transition: var(--outline-transition);
}
:target {
scroll-margin-block: var(--anchor-offset, 6ex);
}
:where(html) {
block-size: 100%;
font-family: var(--family-system);
tab-size: 4;
-webkit-text-size-adjust: none;
text-size-adjust: none;
scrollbar-gutter: stable;
}
@media (prefers-reduced-motion: no-preference) {
:where(html):has(:target) {
scroll-behavior: smooth;
}
}
:where(body) {
min-block-size: 100%;
margin: 0;
background-color: var(--root-bg);
color: var(--root-fg);
font-size: var(--font-size, 1rem);
line-height: var(--line-height, 1.6);
}
:where(:any-link,
button,
input,
label[for],
select,
summary,
textarea,
[tabindex]:not([tabindex^="-"])) {
touch-action: manipulation;
}
:where(:any-link,
button,
label[for],
label:has(input:is([type=radio], [type=checkbox]):enabled),
select:has(option:enabled),
summary) {
cursor: pointer;
}
:where([type=button], [type=submit], [type=reset], button),
::file-selector-button {
appearance: button;
}
:where(summary) {
display: list-item;
}
:where(summary > *) {
display: inline;
}
:disabled {
cursor: not-allowed;
}
:where(img, svg, video, canvas, audio, iframe, embed, object) {
display: block;
max-inline-size: 100%;
}
:where(img, svg, iframe) {
block-size: auto;
}
:where(iframe) {
aspect-ratio: var(--aspect-ratio, 16/9);
}
[hidden]:not([hidden=until-found]) {
display: none;
}
:root {
--text-flow: 1em 1rem;
--separator-flow: 2.5em;
--list-flow: 0.5em;
--h-size: 1.125rem;
--h-weight: 700;
--h-line: 1.1;
--h1-size: 3rem;
--h2-size: 2rem;
--h3-size: 1.625rem;
--h4-size: 1.375rem;
}
:where(h1, h2, h3, h4, h5, h6, p, dl, ul, ol, pre, hgroup) {
margin-block: var(--text-flow);
}
:where(blockquote, figure, hr) {
margin-block: var(--separator-flow);
margin-inline: 0;
}
:where(h1, h2, h3, h4, h5, h6) {
font-size: var(--h-size);
font-weight: var(--h-weight);
line-height: var(--h-line);
text-wrap: balance;
}
:where(h1) {
--h-size: var(--h1-size);
}
:where(h2) {
--h-size: var(--h2-size);
}
:where(h3) {
--h-size: var(--h3-size);
}
:where(h4) {
--h-size: var(--h4-size);
}
:where(p, li, dd) {
text-wrap: pretty;
}
:where(:any-link) {
color: inherit;
}
:where(mark, :not(pre) > code) {
padding: 0.0625em 0.25em;
}
:where(blockquote) {
border-inline-start: 4px solid;
padding-inline-start: 1.5em;
padding-block: 0.25em;
}
:where(pre) {
padding: 0.5em 1em;
white-space: pre;
overflow: auto;
}
:where(input, button, textarea, select, address),
::file-selector-button {
font: inherit;
}
:where(:is(ul, ol)[role=list], nav :is(ul, ol)) {
--list-flow: 0;
list-style: none;
margin-block: 0;
padding-inline-start: 0;
}
:where(hgroup > *),
:where(dl, ol, ul) :where(dl, ol, ul) {
margin-block: 0;
}
:where(dt) {
font-weight: 500;
}
:where(li + li, dd + dt) {
margin-block-start: var(--list-flow);
}
:where(dd) {
margin-inline-start: 0;
}
:where(hr) {
height: auto;
border-block-end-width: 2px;
color: inherit;
}
:where(sub, sup) {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
:where(sub) {
bottom: -0.25em;
}
:where(sup) {
top: -0.5em;
}
:where(code, pre, kbd, samp) {
font-family: var(--family-mono);
}
:where(code, pre) {
writing-mode: lr;
direction: ltr;
}
:where(pre, :not(pre) > code) {
border-radius: var(--code-radius, 0.3em);
border: 1px solid var(--code-stroke, rgba(0, 0, 0, 0.2));
background: var(--code-bg, var(--surface-color));
color: var(--code-color);
}
:where(table) {
border-spacing: 0;
border-color: var(--table-stroke, #777);
}
:where(thead, tbody, tfoot, tr, th, td) {
border-color: inherit;
text-align: inherit;
}
:where(th, td) {
padding-block: var(--table-py, 0.875em);
padding-inline: var(--table-px, 0.625em);
background: var(--root-bg);
color: var(--root-fg);
}
:where(tr + tr :is(th, td)) {
border-block-start-width: 1px;
}
:where(thead tr:last-child :is(th, td)) {
border-block-end-width: 2px;
}
:where(tfoot tr:first-child :is(th, td)) {
border-block-start-width: 2px;
}
:where(input:not([type=range], [type=button], [type=reset], [type=submit]), textarea, select, .form-input) {
appearance: none;
margin: 0;
padding-block: var(--form-py, 0.4375rem);
padding-inline: var(--form-px, 0.8rem);
border-width: var(--form-border-width, 1px);
border-color: var(--form-stroke);
border-radius: var(--form-radius, 0.35rem);
background: var(--form-bg, var(--root-bg));
color: var(--form-color, var(--root-fg));
transition: color 250ms, background-color 250ms, border-color 250ms, box-shadow 250ms;
}
:where(input:not([type=range], [type=button], [type=reset], [type=submit]), textarea, select):placeholder-shown {
text-overflow: ellipsis;
}
:where(input:not([type=range], [type=button], [type=reset], [type=submit]), textarea, select):focus-visible {
border-color: var(--form-active-color);
}
:where(input:not([type=range], [type=button], [type=reset], [type=submit]), textarea, select):disabled {
box-shadow: none;
cursor: not-allowed;
}
:where(input:not([type=checkbox], [type=radio], [type=range], [type=button], [type=reset], [type=submit]), textarea, select) {
--outline-size: 1px;
--outline-offset: calc(
(var(--form-border-width, 1px) + 1px) * -1
);
display: block;
max-inline-size: 100%;
inline-size: 100%;
}
:where(input:not([type=checkbox], [type=radio], [type=range], [type=button], [type=reset], [type=submit]), textarea, select):is(:disabled, [readonly]:not(:focus)) {
border-style: var(--form-disabled-border-style, dashed);
}
:where(input[type=checkbox], input[type=radio]) {
--_gap: var(--icon-gap, 2px);
block-size: var(--control-size, 1.25em);
inline-size: var(--control-size, 1.25em);
border-radius: var(--control-radius, 0.35rem);
border-width: var(--control-border-width, 2px);
padding: var(--_gap);
user-select: none;
}
:where(input[type=checkbox], input[type=radio]):not(:disabled) {
cursor: pointer;
}
:where(input[type=checkbox], input[type=radio])::after {
content: "";
display: block;
block-size: 100%;
inline-size: 100%;
border-radius: inherit;
background-color: var(--icon-color, currentcolor);
scale: var(--_show-icon, 0);
opacity: var(--_show-icon, 0);
transition: translate 0.15s, scale 0.15s, opacity 0.15s;
forced-color-adjust: none;
-webkit-mask: var(--_icon) center/contain no-repeat;
mask: var(--_icon) center/contain no-repeat;
}
:where(input[type=checkbox], input[type=radio]):checked {
--_show-icon: 1;
outline-color: var(--control-checked-stroke, var(--root-fg));
border-color: var(--control-checked-stroke, var(--root-fg));
background-color: var(--control-checked-bg, var(--root-fg));
color: var(--control-checked-color, var(--root-bg));
}
:where(input[type=radio]) {
--_gap: 3px;
--control-radius: 50%;
}
:where(input[type=checkbox]:not([role=switch])) {
--_gap: 1px;
--_icon: url("#");
--checked-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='4'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E %3C/svg%3E");
--indeterminate-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='4'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E");
}
:where(input[type=checkbox]:not([role=switch])):indeterminate {
--_show-icon: 1;
--_icon: var(--indeterminate-icon);
}
:where(input[type=checkbox]:not([role=switch])):checked {
--_icon: var(--checked-icon);
}
:where(input[type=checkbox][role=switch]) {
--_show-icon: 1;
--control-radius: 1.25em;
inline-size: var(--switch-size, 2em);
}
:where(input[type=checkbox][role=switch])::after {
inline-size: auto;
aspect-ratio: 1;
}
:where(input[type=checkbox][role=switch]):checked {
--_offset-x: calc(
var(--switch-size, 2em) -
var(--control-size, 1.25em)
);
}
:where(input[type=checkbox][role=switch]):checked::after {
translate: var(--_offset-x) 0%;
}
:where(input[type=checkbox][role=switch]):checked:dir(rtl)::after {
translate: calc(var(--_offset-x) / -1) 0%;
}
:where(input[type=color]) {
--form-py: 3px;
--form-px: 3px;
block-size: var(--block-size, 2.5em);
inline-size: var(--inline-size, 2.5em);
}
:where(input[type=color])::-webkit-color-swatch-wrapper {
padding: 0;
border-radius: inherit;
}
:where(input[type=color])::-moz-color-swatch {
border: 0;
border-radius: inherit;
}
:where(input[type=color])::-webkit-color-swatch {
border: 0;
border-radius: inherit;
}
:where(input[type=file]) {
--form-py: 0;
--form-px: 0 0.8rem;
--btn-radius: 0;
--btn-border-width: 0;
overflow: clip;
}
:where(input[type=file])::file-selector-button {
margin-inline-end: 1em;
}
:where(select) {
inline-size: auto;
word-wrap: normal;
}
:where(select:not([size]:not([size="1"]), [multiple])) {
--icon-size: var(--select-icon-size, 1.25em);
--icon-offset: var(--select-icon-offset, 0.8rem);
padding-inline-end: calc(var(--icon-size) + var(--icon-offset) + 0.5ch);
background: var(--select-icon, var(--select-light-scheme)) var(--form-bg, var(--root-bg)) var(--icon-position, right) var(--icon-offset) center/var(--icon-size) no-repeat;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
:where(select:not([size]:not([size="1"]), [multiple])):dir(rtl) {
--icon-position: left;
}
:where(textarea) {
resize: vertical;
}
:where(textarea:not([row])) {
min-block-size: var(--textarea-min-size, calc(3.5lh + var(--form-py, 0.4375rem)));
field-sizing: content;
}
::placeholder {
opacity: 1;
color: color-mix(in srgb, currentcolor 54%, transparent);
}
:where(fieldset) {
min-inline-size: 0;
margin: 0;
padding: 0;
}
:where(legend) {
padding: 0;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-datetime-edit,
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-year-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-minute-field,
::-webkit-datetime-edit-second-field,
::-webkit-datetime-edit-millisecond-field,
::-webkit-datetime-edit-meridiem-field {
display: inline;
padding-block: 0;
}
::-webkit-date-and-time-value {
min-height: 1lh;
text-align: inherit;
}
::-webkit-calendar-picker-indicator {
line-height: 1;
}
:where([list]:not([type*=date], [type=month], [type=week], [type=time])) {
--datalist-icon: var(--select-icon, var(--chevron-light-scheme));
}
:where([list]:not([type*=date], [type=month], [type=week], [type=time]))::-webkit-calendar-picker-indicator {
display: block ;
background: var(--datalist-icon) center/contain no-repeat;
opacity: 1;
}
:where([list]:not([type*=date], [type=month], [type=week], [type=time]))::-webkit-list-button {
appearance: none;
block-size: 1em;
inline-size: 1em;
padding: 3px;
align-self: center;
background: var(--datalist-icon) center/contain no-repeat;
}
:where(button, [type=button], [type=submit], [type=reset], .btn),
::file-selector-button {
display: inline-flex;
justify-content: center;
align-items: center;
gap: var(--btn-gap, 0.5em);
padding-block: var(--btn-py, 0.375rem);
padding-inline: var(--btn-px, 0.8rem);
border-radius: var(--btn-radius, 0.5rem);
border-width: var(--btn-border-width, 2px);
border-color: var(--btn-stroke);
background: var(--btn-bg, transparent);
color: var(--btn-color, currentcolor);
font-weight: var(--btn-font-weight, 500);
text-decoration: none;
transition: color 0.2s, background-color 0.2s, border-color 0.2s, box-shadow 0.2s, var(--outline-transition);
user-select: none;
cursor: pointer;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
:where(button, [type=button], [type=submit], [type=reset], .btn) {
vertical-align: middle;
}
:where(button, [type=button], [type=submit], [type=reset], .btn):hover,
::file-selector-button:hover {
border-color: var(--btn-hover-stroke, var(--btn-stroke));
background-color: var(--btn-hover-bg, var(--btn-bg, color-mix(in srgb, currentcolor, transparent 86%)));
color: var(--btn-hover-color, var(--btn-color, currentcolor));
}
:where(button, [type=button], [type=submit], [type=reset], .btn):is(:active, .is-active, [aria-current=page], [aria-current=true], [aria-selected=true], :where(:has(input:checked))) {
border-color: var(--btn-active-stroke, var(--btn-stroke));
background-color: var(--btn-active-bg, var(--btn-bg, color-mix(in srgb, currentcolor, transparent 78%)));
color: var(--btn-active-color, var(--btn-color, currentcolor));
}
:where(button, [type=button], [type=submit], [type=reset], .btn):is(:disabled [aria-disabled=true]),
:disabled::file-selector-button {
box-shadow: none;
border-color: var(--btn-disabled-stroke);
background-color: var(--btn-disabled-bg, color-mix(in srgb, var(--root-bg) 90%, var(--root-fg)));
color: var(--btn-disabled-color, color-mix(in srgb, var(--root-fg) 40%, var(--root-bg)));
cursor: not-allowed;
}
:where(dialog) {
--backdrop: hsla(0, 0%, 0%, 0.3);
--ty: var(--dialog-translate-y, 2rem);
--tx: var(--dialog-translate-x, 0);
--speed: var(--dialog-close-speed, 300ms);
--screen-y: 2rem;
--screen-x: 2rem;
--my: auto;
--mx: auto;
position: fixed;
max-block-size: calc(100% - var(--screen-y));
max-inline-size: calc(100% - var(--screen-x));
background-color: var(--dialog-bg, color-mix(in oklab, var(--root-bg) 88%, white));
color: var(--dialog-color, var(--root-fg));
border-radius: 1rem;
margin-block: var(--my);
margin-inline: var(--mx);
padding: 1.5rem;
box-shadow: var(--dialog-shadow, 0 3px 5px hsla(0, 0%, 0%, 0.18));
translate: var(--tx) var(--ty);
opacity: 0;
}
:where(dialog)::backdrop {
background-color: var(--backdrop);
}
@media (prefers-reduced-motion: no-preference) {
:where(dialog) {
transition-property: translate, opacity, display, overlay;
transition-duration: var(--speed);
transition-behavior: allow-discrete;
}
}
:where(dialog:is([open], :popover-open)) {
--speed: var(--dialog-open-speed, 400ms);
opacity: 1;
translate: 0 0;
}
@starting-style {
:where(dialog:is([open], :popover-open)) {
opacity: 0;
translate: var(--tx) var(--ty);
}
}
:where(:root:has(dialog[open]:modal)) {
overflow: hidden;
}
@media print {
:root {
--root-bg: white;
--root-fg: black;
}
:is(h1, h2, h3, h4, h5, h6) {
page-break-after: avoid;
}
:is(h2, h3, h4, h5, h6, p) {
orphans: 3;
widows: 3;
}
:is(tr, img, svg, pre, blockquote) {
page-break-inside: avoid;
}
abbr[title]::after {
content: " (" attr(title) ")";
}
a[href^="https://"]::after {
content: " (" attr(href) ")";
}
thead {
display: table-header-group;
}
:is(input, select, textarea, button, .btn) {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}