// VERSION: 0.1.0
// @use 'sass:color';
// IMPORT OTHER STYLESHEETS
// @use './blog';
// DEFINE CSS LAYER ORDERS (lowest -> highest priority)
// - tailwind-theme: for tailwind theme
// - tailwind-base: for tailwind base
// - base: for global base styles
// - subutilities: for custom utilities that can be overridden by tailwind utilities
// - tailwind-utilities: for tailwinds main classes.
@layer low-level, tailwind-theme, tailwind-base, base, subutilities, tailwind-utilities, utilities;
// @import url('https://fonts.googleapis.com/css2?family=Dosis:wght@200..800&family=Fredoka:wght@300..700&family=Quicksand:wght@300..700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Dosis:wght@200..800&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Delius&display=swap');
@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
// Font awesome icon fonts.
// NOTE: you can use font awesome icons as either text or SVG.
// Use as svg by adding a class name to the element container.
// Use as text by setting the unicode character associated with the icon
// + setting the font, using:
// font: var(--fa-font-solid); // For solid icons.
// font: var(--fa-font-regular); // For outline icons.
// font: var(--fa-font-brands); // For brand icons.
// NOTE: the `--fa-font-brands` variables are automatically defined by the
// font awesome CSS file.
//
// WARNING: It will actually import aditional font files (.woff2 files) for the different font-sets.
// BUT, it only donwloads them lazily when they are ACTUALLY visible on the page.
// Even if you try to pre-fetch the icons using the following:
// @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/webfonts/fa-solid-900.woff2");
// It will not actually download the fonts (due to a strict cross-origin policy).
//
// So, if you would like pre-fetching to work, you could create a shadow
// pseudo element `::after` with `content ""` and `font: var(--fa-font-solid);`
// This will force that font set to be downloaded ahead of time.
@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css"); // All the CSS (~16Kb)
// @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/solid.min.css"); // Only the CSS for solid icons (0.9kb)
// @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/regular.min.css"); // Only the CSS for outline icons (0.9kb)
// @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/brands.min.css"); // Only the CSS for brand icons (4.4kb)
// @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/svg.min.css"); // ???? (1.6Kb)
// =============================================================================
// THEME VARIABLES
// =============================================================================
$font-title: 'Dosis', sans-serif;
$font-body: 'Roboto', sans-serif;
// $font-body: 'Quicksand', sans-serif;
// $font-body: 'Dosis', sans-serif;
// $font-body: 'Fredoka', sans-serif;
// $font-body: 'Delius', sans-serif;
$themeDark1: #1f2937;
// $themeGreen1: #82C13A;
$themeGreen1: #67b654;
$themeBlue1: #3498db;
$themeRed1: #dc3545;
$themeYellow1: #cc9227;
$themeColorPrimary: $themeGreen1;
$themeColorSecondary: $themeBlue1;
$themeColorWarning: $themeYellow1;
$themeColorError: $themeRed1;
$themeColorDanger: $themeRed1;
$themePageBgColor: #dae2b5;
// $themeTextColor: #414641;
$themeTextColor: #3a423a;
// $themeTextColor: #d852dd;
// # RONNY: TODO: define these link color variants as modifications of the base colors
$linkColor: $themeBlue1;
$linkColorHover: rgb(25, 190, 80);
$linkColorActive: rgb(36, 116, 20);
$inputColorBg: white;
$inputColorInactiveBg: rgba(from #d6d3d3 r g b / 0.5);
$inputColorActive: $themeGreen1;
$inputColorPillLabelBg: $themeDark1;
$inputColorPillLabelText: white;
$inputColorWarn: $themeYellow1;
$inputColorDanger: $themeRed1;
$inputBorderRadius: 1rem;
$buttonRadius: 1rem;
$buttonColorPrimary: $themeGreen1;
$buttonColorSecondary: $themeBlue1;
$buttonColorDanger: $themeRed1;
$buttonColorWarning: $themeYellow1;
// =============================================================================
// THEME VARIABLES
// =============================================================================
@layer base {
:root {
// Theme colors
--color-dark1: #{$themeDark1};
--color-green1: #{$themeGreen1};
--color-blue1: #{$themeBlue1};
--color-red1: #{$themeRed1};
--color-yellow1: #{$themeYellow1};
// Purpose colors
--color-text: #{$themeTextColor};
--color-primary: #{$themeColorPrimary};
--color-secondary: #{$themeColorSecondary};
--color-warning: #{$themeColorWarning};
--color-error: #{$themeColorError};
--color-danger: #{$themeColorDanger};
// Defualt fallbacks
--default-border-radius: #{$inputBorderRadius};
--default-padding-x: 1rem;
--default-padding-y: 1rem;
--default-padding: var(--default-padding-y) var(--default-padding-x);
--default-line-height: 1.5em;
// Global Style Defaults
--border-radius: var(--default-border-radius);
--padding-x: var(--default-padding-x);
--padding-y: var(--default-padding-y);
--padding: var(--padding-y) var(--padding-x);
--line-height: var(--default-line-height);
// LISTS
--ordered-list-label-color: var(--color-primary);
// RONNY: TODO: add bullet color varaible for unordered list.
// EXPANDABLE SECTIONS
// Expandable sections are (details>summary elements)
--expandable-marker-bg-color: var(--color-primary);
--expandable-marker-bg-radius: 0.25em;
--expandable-summary-bg-color: unset;
--expandable-expanded-bg-color: unset;
--expandable-border-radius: 0px;
--expandable-expanded-border-width: 0px;
--expandable-summary-padding: 0px;
--expandable-expanded-padding: 1px 0px; // NOTE: Set vertical padding to something bigger than 0. Otherwise you get unexpected margins in expanded content.
--expandable-gap: 0.1rem; // Gap between subsequent expandable sections (summary labels).
// Block variant of expandable
--expandable-block-summary-bg-color: var(--expandable-marker-bg-color);
--expandable-block-expanded-bg-color: white;
--expandable-block-border-radius: 0.5rem;
--expandable-block-expanded-border-width: 2px;
--expandable-block-summary-padding: 0.5em;
--expandable-block-expanded-padding: 0.5em 0.5em; // NOTE: Set vertical padding to something bigger than 0. Otherwise you get unexpected margins in expanded content.
--expandable-block-gap: 0.1rem; // Gap between subsequent expandable sections (summary labels).
// Formatted text block styles
--formatted-code-text-color: #b9f59e; // pastel green
--formatted-code-bg-color: #{$themeDark1};
--formatted-code-padding: 2rem 2rem;
--formatted-sample-output-text-color: rgb(231, 224, 224);
--formatted-sample-output-error-text-color: #{$themeColorError};
--formatted-sample-output-warn-text-color: #{$themeColorWarning};
// Inline code styles
--inline-code-text-color: #{$themeColorPrimary};
--inline-code-bg-color: #{$themeDark1};
--inline-keyboard-text-color: white;
--inline-keyboard-bg-color: #{$themeDark1};
--inline-code-padding: 0.1em 0.5em;
// Checkboxes
--checkbox-tick-color: white;
--checkbox-border-color: #{$inputColorActive};
--checkbox-selected-bg-color: hsl(from var(--checkbox-border-color) h s calc(l + 10));;
--checkbox-size: 1.5rem;
--checkbox-border-width: 2px;
--checkbox-border-radius: 0.3rem;
--checkbox-bg-color: #{$inputColorBg};
--checkbox-focus-color: #{$inputColorActive}; // The color when the checkox is the currently focused element.
// TODO: radio buton variables.
}
}
// =============================================================================
// BASE ELEMENT STYLES
// =============================================================================
@layer base {
html {
--primary-color: #{$themeGreen1};
background-color: $themePageBgColor;
color: $themeTextColor;
// font-family: $font-faSolid;
// font: var(--fa-font-solid);
font-family: $font-body;
font-weight: 500;
font-size: 1.0rem;
// CUSTOM SCROLL BARS FOR PAGE
// // scrollbar-gutter: stable; // Always reserves space for scrollbar to prevent content from shifting. NOTE: looks weird at page level.
// // Benefit of using a transparent track is that it does not break any rounded corners design.
// scrollbar-color: var(--primary-color) transparent; // thumb-color, track color
scrollbar-color: var(--primary-color) $themeDark1; // thumb-color, track color
scrollbar-width: auto; // auto | thin
// reset the scrollbar values for all child elements
& * {
scrollbar-color: auto;
scrollbar-width: auto;
}
// // Fallback for older webkit/chrome browsers.
// // Actually gives you more control. But only available on webkit/chrome browsers.
// // and the `scrollbar-color` and `scrollbar-width` properties take higher precedence.
// &::-webkit-scrollbar {
// width: 1.5em;
// }
// &::-webkit-scrollbar-track {
// // box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
// // background-color: transparent;
// background-color: $themeDark1;
// }
// &::-webkit-scrollbar-thumb {
// background-color: var(--primary-color);
// outline: none;
// border: none;
// border-radius: 0.5em;
// }
&::after {
// Pre-load the fontawesome icons.
// font: var(--fa-font-regular);
font: var(--fa-font-solid);
content: ""
}
}
body {
margin: 0px;
/* margin: 0 auto; */
/* margin: 0 auto; */
/* width: 100%; */
/* max-width: 80ch; */
padding: 3.3rem 0px 0px 0px;
line-height: 1.5;
/* min-height: 100vh; */
display: flex;
flex-direction: column;
min-height: 100vh;
}
// RONNY: TODO: This part seems like an app specific thing. Put it in a different section.
.body-content {
max-width: 80ch;
margin: 0 auto;
flex-grow: 1;
flex-shrink: 0;
flex-basis: auto;
}
*, *::before, *::after {
box-sizing: border-box;
}
h1, h2, h3, h4, h5, h6 {
font-family: $font-title;
color: $themeColorPrimary;
}
h1 {
margin: 1rem 0;
font-size: 2.5rem;
}
hr {
border-width: 0px;
background-color: $themeColorPrimary;
height: 1px;
width: 100%;
}
a, a:visited{
text-decoration: none;
color: $linkColor;
}
a:hover {
color: $linkColorHover;
}
// WARNING: The :active pseudo tag MUST be placed AFTER the :hover pseudo tag.
// Otherwise, the :hover pseudo tag will override the :active pseudo tag.
a:active {
color: $linkColorActive;
}
// LISTS
// Define bullet variables that can inherit from parent, and with a default fallback.
@property --bullet-icon {
syntax: "<string>";
inherits: true;
initial-value: "\f0c8"; // square
}
@property --bullet-weight {
syntax: "<number>";
inherits: true;
initial-value: 900; // solid
}
// List variables
ol, ul {
--gap-y: 0.5em;
--indent: 1.25rem;
}
ul {
--bullet-gap: 0.25rem;
--bullet-size: 1rem;
--bullet-color: var(--color-primary);
--bullet-icon: var(--bullet-icon); // Inherit from parent, or fallback to dafault defined in @property
--bullet-weight: var(--bullet-weight); // Inherit from parent, or fallback to dafault defined in @property
}
// Generic List Styling
ol, ul {
margin: unset;
padding: unset; // remove padding from top level items.
list-style-position: inside;
li {
padding: unset;
margin-bottom: var(--gap-y);
margin-top: var(--gap-y);
padding-left: 0px;
padding-inline-start: 0px;
}
}
// Styling for nested list items.
li ol li, li ul li {
padding: unset;
// Apply padding only to nested list items.
padding-left: var(--indent);
margin: unset;
margin-top: var(--gap-y);
margin-bottom: var(--gap-y);
}
// ul li, li ul li {
// // Fudge factor, margin at top that only applies to unoordered lists.
// // margin-top: 0.5em;
// }
ul {
&.circle {--bullet-icon: "\f111"; --bullet-weight: 900;} // circle, solid
&.disc {--bullet-icon: "\f111"; --bullet-weight: 400;} // circle, regular
&.square {--bullet-icon: "\f0c8"; --bullet-weight: 900;} // square, solid
&.square-outline {--bullet-icon: "\f0c8"; --bullet-weight: 400;} // square, regular
&.chevron-circle {--bullet-icon: "\f138"; --bullet-weight: 900;} // circle-chevron-right, solid
&.chevron {--bullet-icon: "\f054"; --bullet-weight: 900;} // chevron-right, solid
&.caret {--bullet-icon: "\f0da"; --bullet-weight: 900;} // caret-right, solid
&.star {--bullet-icon: "\f005"; --bullet-weight: 900;} // star, solid
list-style-type: none; // remove the default bullets.
// max-inline-size:30em; // can set max width of list. then wraps around.
// // can lead to very nice styling.
li {
// &::marker {} // ::marker does not allow as much customization as ::before.
&::before {
content: var(--bullet-icon);
font: var(--fa-font-solid);
color: var(--bullet-color);
margin-right: var(--bullet-gap);
font-size: 1rem;
line-height: 1em;
font-weight: var(--bullet-weight);
}
}
}
// ---------------------------------------------------------------------------
// EXPERIMENTAL LIST SETTINGS to get list content flush with icons right edge.
// taken from: https://piccalil.li/blog/an-in-depth-guide-to-customising-lists-with-css/
// Looks very promising.
// styling for unordered lists.
ul {
padding-inline-start: calc(var(--bullet-size) + var(--bullet-gap)); // Shifts the entire thing to right, to account for bulllets being to the left of edge.
// line-height:1.5em;
li {
// margin-block-end:1lh;
// min-block-size: 2lh;
position:relative;
text-box: trim-start cap alphabetic; // QUESTION: What does this actually do?
&::before {
position:absolute;
margin: 0px;
// inset-block-start: 0.25em; // Only needed if line height is not 1.5. Shift the bullet vertically. In chrome it requires shifting up, in Firefox it requires shifting down.
inset-inline-start: calc(-1* (var(--bullet-size) + var(--bullet-gap))); // Controls gap between bullet and text, by shifting the bullet to the left.
inline-size:0em; // Also shifts the bullet horizontally? But only can in positive direction, moving closer to text.
text-box: trim-start cap alphabetic; // QUESTION: Does this actually do anything.
text-align:end;
font-size:var(--bullet-size);
line-height:1.5;
}
}
}
// Styling for nested list items.
li ul {
padding-inline-start: calc(var(--indent)); // Shifts the entire thing to right, to account for bulllets being to the left of edge.
translate: calc(-1 * var(--indent)) 0px; // hacky tweak to shift the entire thing to the left (which padding cannot do).
li {
&::before {
inset-inline-start: calc(var(--indent) - 1* (var(--bullet-size) + var(--bullet-gap))); // gap between bullet and text. But chifts the bullet, not the text.
}
}
}
// ---------------------------------------------------------------------------
ol {
--label-width: 1em;
--label-gap: 0.5em;
--label-color: var(--ordered-list-label-color);
counter-reset: itemCount;
list-style-type: none; // remove the default numbering.
li {
counter-increment: itemCount;
list-style: none;
// ::marker is less customizatble that ::before.
&::before{
content: counter(itemCount) ". ";
min-width: var(--label-width);
// background-color: fuchsia;
font-weight: bold;
display: inline-block;
color: var(--label-color);
margin-right: var(--label-gap);
}
}
// For numbers that inherit the parent numbers.
// e.g. 4.2.6 (instead of 6, for the item 3 levels deep).
&.nested-numbers, // apply to current level ordered list
&.nested-numbers ol // also apply to nested ordered lists
{
// background-color: orange;
li {
&::before {
// Formatting for the numbers.
content: counters(itemCount, ".") ". ";
}
}
}
&.number-pills,
&.number-pills ol
{
--label-width: 3.5em;
// background-color: pink;
li {
margin-bottom: 0.5rem;
}
li::before {
// min-width: var(--width);
border-radius: 0.75em;
padding: 0.5rem 0.5rem 0.5rem 0.5rem;
margin: 2px var(--label-gap) 2px 0px;
// justify-content: center; // center the number horizontally.
text-align: center;
color: white;
background-color: var(--ordered-list-label-color);
}
}
}
// -------------------
// EXPANDABLE SECTIONS
// -------------------
details {
&.block {
// Block variant of expandable. overrides some of the default styles.
--expandable-summary-bg-color: var(--expandable-block-summary-bg-color);
--expandable-expanded-bg-color: var(--expandable-block-expanded-bg-color);
--expandable-border-radius: var(--expandable-block-border-radius);
--expandable-expanded-border-width: var(--expandable-block-expanded-border-width);
--expandable-summary-padding: var(--expandable-block-summary-padding);
--expandable-expanded-padding: var(--expandable-block-expanded-padding); // NOTE: Set vertical padding to something bigger than 0. Otherwise you get unexpected margins in expanded content.
--expandable-gap: var(--expandable-block-gap);
}
> summary {
background-color: var(--expandable-summary-bg-color);
border-radius: var(--expandable-border-radius);
margin-top: var(--expandable-gap);
padding: var(--expandable-summary-padding);
}
> div, > pre {
background-color: var(--expandable-expanded-bg-color);
margin: 0px;
padding: 1px 0px; // NOTE: Set vertical padding to something bigger than 0. Otherwise you get unexpected margins in expanded content.
padding: var(--expandable-expanded-padding); // NOTE: Set vertical padding to something bigger than 0. Otherwise you get unexpected margins in expanded content.
border: var(--expandable-expanded-border-width) solid var(--expandable-summary-bg-color);
}
&[open].block > summary{
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
&[open].block > div, &[open].block > pre{
border-top-right-radius: 0px;
border-top-left-radius: 0px;
border-bottom-left-radius: var(--expandable-border-radius);
border-bottom-right-radius: var(--expandable-border-radius);
}
}
// Configure the style and behaviour of the arrow marker icon for expandable sections.
details:not(.right, .right-edge) >summary::before,
details.right > summary::after,
details.right-edge > summary::after {
font: var(--fa-font-solid);
content: "\f078"; // chevron-down, solid
padding: 0.5em;
background-color: var(--expandable-marker-bg-color);
// border-radius: 0.25em;
border-radius: var(--expandable-marker-bg-radius);
transition: transform 0.1s ease-in-out;
}
details:not(.right, .right-edge) > summary:hover::before,
details.right > summary:hover::after ,
details.right-edge > summary:hover::after {
color: white;
};
details:not(.right, .right-edge) > summary:active::before,
details.right > summary:active::after,
details.right-edge > summary:active::after {
color: unset;
transform: scale(0.9); // Slight animation when clicked.
transition: transform 0.1s ease-in-out;
};
details[open]:not(.right, .right-edge) > summary::before,
details[open].right > summary::after,
details[open].right-edge > summary::after {
content: "\f077"; // chevron-up, solid
}
summary {
cursor: pointer;
list-style-position: inside;
display: flex;
align-items: baseline;
gap: 0.5rem;
&::marker {
// The default arrow marker is contained in a pesudo-element called
// ::marker, but doesnt behave very nicely for the folllowing scenarios:
// - When the summary text is long and wraps around to multiple lines.
// - When you want to add a custom gap between the marker and the summary
// text.
// So, using a ::before (or ::after) for the arrow marker is nicer.
// Set it to an empty string because we are using ::before and ::after instead.
content: "";
}
}
details.right-edge > summary {
// Make the label align to the left edge, and the arrow marker align to the right edge.
justify-content: space-between
}
// ----------------------------
// INLINE FORMATTED TEXT BLOCKS
// ----------------------------
code, kbd, var, samp {
font-family: monospace;
// background-color: transparent;
border-radius: 0.25em;
padding: var(--inline-code-padding);
color: var(--inline-code-text-color);
}
code {
background-color: var(--inline-code-bg-color);
}
kbd {
background-color: var(--inline-keyboard-bg-color);
color: var(--inline-keyboard-text-color);
}
var {
background-color: transparent;
padding: 0px;
}
samp {
&.error {
color: var(--formatted-sample-output-error-text-color);
}
&.warn {
color: var(--formatted-sample-output-warn-text-color);
}
background-color: var(--inline-code-bg-color);
color: var(--formatted-sample-output-text-color);
}
// Multiline formatted text blocks
pre {
white-space: pre;
tab-size: 2;
font-family: monospace;
font-size: 1em;
line-height: 1.5;
padding: none;
> code, kbd, var, samp {
all: unset;
}
&:has(code, kbd, samp, var) {
border-radius: var(--border-radius);
padding: var(--formatted-code-padding);
background-color: var(--formatted-code-bg-color);
color: var(--formatted-code-text-color);
}
&:has(samp) {
color: var(--formatted-sample-output-text-color);
&.error, & > samp.error {
color: var(--formatted-sample-output-error-text-color);
}
&.warn, & > samp.warn {
color: var(--formatted-sample-output-warn-text-color);
}
}
}
// -------------------
// FORM INPUT ELEMENTS
// -------------------
form, input, textarea, fieldset, select {
--padding-y: 1rem;
--padding-x: 1rem;
--font-size: 1rem;
--line-height: 1.5em;
}
// input type number, or text, or password, etc.
input, textarea, select {
--bg-color: #{$inputColorBg};
--inactive-bg-color: #{$inputColorInactiveBg};
--text-color: #{$themeTextColor};
--active-color: #{$inputColorActive};
--warn-color: #{$inputColorWarn};
--border-radius: #{$inputBorderRadius};
}
textarea,
select,
input[type="number"], input[type="text"], input[type="password"], input[type="email"],
input[type="url"], input[type="tel"], input[type="date"], input[type="time"], input[type="datetime-local"],
input[type="month"], input[type="week"], input[type="search"] {
border: 2px solid var(--bg-color);
border-radius: var(--border-radius);
padding: var(--padding-y) var(--padding-x);
font-size: var(--font-size);
font-weight: 500;
color: var(--text-color);
outline: none;
line-height: var(--line-height);
&::placeholder {
color: rgba(from var(--text-color) r g b / 0.5);
}
&:disabled::placeholder {
color: rgba(from var(--text-color) r g b / 0.4);
}
&:disabled {
background-color: var(--inactive-bg-color);
border-color: transparent;
// border: none;
box-sizing: border-box;
color: rgba(from var(--text-color) r g b / 0.4);
}
&:focus {
outline: solid 2px var(--active-color);
outline-offset: -4px;
}
&:invalid {
border-color: var(--warn-color);
outline-offset: -6px;
}
}
// ---------------------------
// TEXT AREA INPUT FORM WIDGET
// ---------------------------
textarea {
background-color: var(--bg-color);
scrollbar-gutter: stable; // Always reserves space for scrollbar to prevent content from shifting.
scrollbar-color: var(--active-color) transparent; // thumb-color, track color
scrollbar-width: auto;
&.autoexpand, &.seamless {
--min-height: 6em;
// field-sizing makes the height expand to fit the contents, instead of
// using scroll bars.
// WARNING: as of 2026-04-09, this is not supported on firefox.
resize: none;
field-sizing: content;
min-height: var(--min-height);
}
&.seamless {
all: unset;
// field-sizing makes the height expand to fit the contents, instead of
// using scroll bars.
// WARNING: as of 2026-04-09, this is not supported on firefox.
// WARNING: will require some JS to get full browser support.
field-sizing: content;
border: none;
font-family: $font-body;
padding: 0px;
// border
background-color: transparent;
// background-color: rgba(254, 240, 240, 0.5);
// // field-sizing makes the height expand to fit the contents, instead of
// // using scroll bars.
// // WARNING: as of 2026-04-09, this is not supported on firefox.
// field-sizing: content;
&:focus {
outline: none;
}
}
&.seamless-transition {
// Make a seamless text area transition to a visaully obvious editable textarea when focused.
&:focus {
outline: green;
background-color: var(--bg-color);
}
}
}
// -----------------------
// LABELS
// -----------------------
// SCENARIO: Where label has the text contents of the label FIRST, before the
// input element widget.
// NOTE: Requires the label contents to be wrapped in a <span> element.
label:has(> span:first-child):has(> input, > select, > fieldset) {
--label-bg-color: transparent;
--label-gap: 1em;
// background-color: orange;
display: flex;
gap: var(--label-gap);
align-items: center;
justify-content: flex-start;
> span {
font-weight: bold;
background-color: var(--label-bg-color);
align-self: stretch;
display: flex;
align-items: center;
}
> *:not(span) {
flex: 1;
}
// PILL LABELS.
// Used to give a pill-like appearance for the label+input element pair.
&.pill {
--label-bg-color: #{$inputColorPillLabelBg}; // bookmark
gap: 0px;
--border-radius: #{$inputBorderRadius};
> span {
color: #{$inputColorPillLabelText};
padding: var(--padding-y) var(--padding-x);
border-top-left-radius: var(--border-radius);
border-bottom-left-radius: var(--border-radius);
}
> *:not(span) {
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
}
}
// NEWLINE LABELS
// For when the label should be in its own separate line from the input element.
&.newline {
flex-wrap: wrap;
gap: 0.75rem;
> span {
// make it take up the full width of the label.
// align-self: stretch;
flex-basis: 100%;
}
}
}
// -----------------------
// COLOR INPUT FORM WIDGET
// -----------------------
input[type="color"] {
-webkit-appearance: none;
appearance: none;
border: 0px solid transparent;
padding: 0px;
border-radius: var(--border-radius);
height: calc(var(--font-size) + (2 * var(--padding-y)));
width: 10rem;
overflow: hidden; /* Ensures swatch follows border-radius */
cursor: cell;
&::-webkit-color-swatch-wrapper {
padding: 0;
}
&::-webkit-color-swatch {
border: none;
// border-radius: 0.5rem;
}
&::-moz-color-swatch {
border: none;
// border-radius: 0.5rem;
}
}
// --------------------------
// SELECT INPUT FORM WIDGET
// --------------------------
// bookmark
select {
&, & option {
// remove the default styling. Specifically the dropdown arrow.
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
background-color: var(--bg-color);
border: 2px solid var(--bg-color);
border-radius: var(--border-radius);
font-size: var(--font-size);
font-weight: 500;
color: var(--text-color);
// Scrollbar styling
scrollbar-gutter: stable; // Always reserves space for scrollbar to prevent content from shifting.
scrollbar-color: var(--active-color) transparent; // thumb-color, track color
scrollbar-width: auto;
// SCENARIO: Dropdown + Multiple Select.
// NOTE: This scenario leads to inconsistent rendering behaviour on
// different browsers.
// SO: Make the syle OBVIOUSLY UGLY to discourage using this combination.
&[multiple][size="1"] {
background-color: red;
border: rgb(139, 2, 2) solid 4px;
color: rgb(255, 123, 255);
&:open {
background-color: fuchsia;
color: turquoise;
}
}
// SCENARIO: Dropdown + Single select.
// This happens when multiple not set, AND size is either not set,
// or set to 1.
&:not([multiple]):is(:not([size]), [size="1"])
{
--arrow-icon-size: 1em;
padding: var(--padding-y) var(--padding-x);
// Add a custom arrow icon for the dropdown.
// Embedded a SVG directly.
// Makes use of a sprite SVG image with both up and down buttons
// Separated by a VERY large gap that is likely to be much bigger than the
// select element height. Then simply slide between both extemes of the
// image when open/close the dropdown.
// And we use padding to center the icon.
background-image: url('data:image/svg+xml;charset=US-ASCII,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 10512"><path d="M140.3 376.8c12.6 10.2 31.1 9.5 42.8-2.2l128-128c9.2-9.2 11.9-22.9 6.9-34.9S301.4 192 288.5 192l-256 0c-12.9 0-24.6 7.8-29.6 19.8S.7 237.5 9.9 246.6l128 128 2.4 2.2z"/><path transform="translate(0, 10000) scale(1, -1) translate(0, -512)" d="M140.3 376.8c12.6 10.2 31.1 9.5 42.8-2.2l128-128c9.2-9.2 11.9-22.9 6.9-34.9S301.4 192 288.5 192l-256 0c-12.9 0-24.6 7.8-29.6 19.8S.7 237.5 9.9 246.6l128 128 2.4 2.2z"/></svg>');
background-repeat: no-repeat;
background-size: var(--arrow-icon-size) auto;
background-position: right var(--padding-x) top var(--padding-y);
padding-right: calc(2 * var(--padding-x) + var(--arrow-icon-size)); // Ensure text doesn't overlap the arrow
// background-color: purple;
&:open {
// 100% position aligns the bottom of the image to the bottom of element container.
// So we back off with some padding to center it.
background-position: right var(--padding-x) top calc(100% - var(--padding-y));
}
// appearance: base-select;
&, &::picker(select) {
appearance: base-select;
}
&::picker(select) {
// Picker is the dropdown options list.
border-top-left-radius: 0px;
border-top-right-radius: 0px;
border-radius: var(--border-radius);
border: 2px solid var(--active-color);
}
// Hide the default dropdown arrow icon.
&::picker-icon {
display: none;
}
&:open {
// Style of main widget when dropdown is opened.
// NOTE: Originally tried to set the bottom corners to have a sharp corner,
// To allow a seamless dropdown.
// BUT: depending on position relative to window, sometimes the
// dropdown appears above, rather than below, which makes
// it look funny. So decided to not change the corners.
// border-bottom-left-radius: 0px;
// border-bottom-right-radius: 0px;
outline: solid 2px var(--active-color);
outline-offset: -4px;
}
// Option Item styling
option {
--padding-y: 0.25em;
}
option, &:focus option {
padding: var(--padding-y) var(--padding-x);
}
}
// SCENARIO: List Selection + Multiple Select.
// Happens when multiple is set, and size has been set and is not 1.
// Happens when multiple is set, and size has NOT been set.
// SCENARIO: List Selection + Single Select.
// Happens when multiple is not set, and size has been set and is not 1.
&[multiple][size]:not([size="1"]),
&[multiple]:not([size]),
&:not([multiple])[size]:not([size="1"])
{
// background-color: orange;
// Padding will be handled by the individual options.
padding: var(--padding-y) 0px;
option, &:focus option {
padding: var(--padding-y) var(--padding-x);
}
// // Base select mode.
// // base-select preserves already selected items wthout needing CTRL button.
// // it also adds a checkmark icon to the selected items.
// // NOTE: Only in chrome and Edge for now.
// // NOTE: messes up with some of my styling (focus styling and height size).
// &, &::picker(select) {
// appearance: base-select;
// }
// // The following can allow you to customize the checkmark icon if you are
// // using base-select mode.
// & option::checkmark {
// content: "\f058"; // fa-circle-check
// font: var(--fa-font-solid);
// color: white;
// }
}
// Common option item styles (for both Single and Multiple Select Mode).
option, &:focus option {
&:hover, &:focus, &:focus-visible {
--hover-color: hsl(from var(--active-color) h s calc(l + 20));
background-color: var(--hover-color);
// box-shadow: inset 0 0 0 1000px var(--hover-color);
// border: none;
}
&:checked {
background-color: var(--active-color);
// NOTE: many browsers set a low-level background-color for checked options that cannot be
// overriden. So we use a box-shadow as a hacky way to set the background-color.
// The 1000px spread value is a large value to make the gradient spread WAY outside
// the bounds of the box, resulting in the box contents being the desired solid color.
box-shadow: inset 0 0 0 1000px var(--active-color);
}
}
}
// --------------------------
// CHECKBOX INPUT FORM WIDGET
// --------------------------
input[type="checkbox"],
label:has(> input[type="checkbox"]:first-child) {
--size-outer: var(--checkbox-size);
--size-inner: var(--checkbox-size);
--focus-color: var(--checkbox-focus-color);
--bg-color: var(--checkbox-bg-color);
--border-radius: var(--checkbox-border-radius);
--border-color: var(--checkbox-border-color);
--border-width: var(--checkbox-border-width);
}
// Label for the checkbox
label:has(> input[type="checkbox"]:first-child) {
// Styles apply ONLY to the <label> element if it has as its first child a
// checkbox.
line-height: var(--size-outer);
// Use a grid display to layout the radio button and label nicely.
display: grid;
// Setup as a grid so label appears next to checkbox.
grid-template-columns: 0.5em auto; // Aditional spacing after first element (checkbox).
gap: var(--size-outer); // Space between checkbox and label.
// margin-top: 0.5em;
}
input[type="checkbox"] {
appearance: none;
display: grid;
place-content: center;
cursor: pointer;
width: var(--size-outer);
height: var(--size-outer);
border: var(--border-width) solid var(--border-color);
background-color: var(--bg-color);
&, &::before {
margin: 0;
padding: 0px;
border-radius: var(--border-radius);
text-align: center;
color: var(--checkbox-tick-color);
}
// The container for the checked inner content (background and tick).
&::before {
content: ""; // This will contain the tick icon later.
display: inline-block;
width: var(--size-inner);
height: var(--size-inner);
border: var(--border-width) solid var(--border-color);
// background-color: hsl(from var(--border-color) h s calc(l + 10));
background-color: var(--checkbox-selected-bg-color);
font: var(--fa-font-solid); // Use FontAwesome solid icons font.
font-size: calc(var(--size-inner) - (2 * var(--border-width))); // Controls the tick icon size.
font-weight: 900; // Use font weight of >500 to use solid FA icons.
transform: scale(0); // Start hidden when not checked.
transition: 100ms transform ease-in-out;
}
&:checked::before {
content: "\f00c"; // The tick FA icon.
transform: scale(1); // Make it big enough to see.
}
&:focus {
outline: 2px solid var(--focus-color);
outline-offset: 2px;
}
}
// --------------------------
// RADIO BUTTONS:
// --------------------------
// NOTE: preferred way to use radio buttons (with labels) to make best use of
// this CSS styling, is using the <input type="radio"> element nested
// inside a <label> element.
// Using this approach also eliminates the need to define an id
// for each radio button, and a `for="someId"` attribute on the label to
// link the radio and label.
//
// <label>
// <input type="radio" name='favDesert' value='jelly' />
// Jelly
// </label>
// <label>
// <input type="radio" name='favDesert' value='iceCream' />
// Ice Cream
// </label>
//
label:has(> input[type="radio"]:first-child), input[type="radio"] {
--size-outer: 1.5rem;
--size-inner: 1.5rem;
// --text-size: 1em;
--focus-color: #{$inputColorActive};
--border-color: #{$inputColorActive};
--radio-bg-color: var(--bg-color);
}
// Label for the radio button.
label:has(> input[type="radio"]:first-child) {
// Styles apply ONLY to the <label> element if it has as its first child a
// radio button.
line-height: var(--size-outer);
// font-size: 1em;
// Use a grid display to layout the radio button and label nicely.
display: grid;
grid-template-columns: 1em auto;
gap: calc(var(--size-outer) / 1.3);
margin-top: 0.5em;
}
// The radio button itself.
input[type="radio"] {
-webkit-appearance: none;
appearance: none;
margin: 0;
background-color: var(--radio-bg-color);
// accent-color: fuchsia; // Sets what exactly?
width: var(--size-outer);
height: var(--size-outer);
border: 2px solid var(--border-color);
border-radius: 50%;
display: grid;
place-content: center;
// Set the inner circle that is shown when the radio button is checked.
&::before {
content: "";
width: var(--size-inner);
height: var(--size-inner);
border-radius: 50%;
transform: scale(0);
transition: 80ms transform ease-in-out;
background-color: var(--focus-color);
// Use box-shadow for better compatibility of state when printing page.
box-shadow: inset var(--size-inner) var(--size-inner) var(--focus-color);
}
&:checked::before {
// Make the inner circle grow when checked.
transform: scale(1);
}
&:focus {
outline: 2px solid var(--focus-color);
outline-offset: 2px;
}
}
// FIELDSET
// Very useful for wrapping a group of radio buttons and adding a legend label.
// Example:
// <fieldset>
// <legend>Favorite Dessert</legend>
// <label>
// <input type="radio" name='favDesert' value='jelly' />
// Jelly
// </label>
// <label>
// <input type="radio" name='favDesert' value='iceCream' />
// Ice Cream
// </label>
// </fieldset>
//
fieldset {
border: none;
// padding: 0;
margin: 0;
border: 2px solid $inputColorActive;
border-radius: 1rem;
padding: 1.5em 1.5em;
&.seamless {
padding: unset;
border: none;
border-radius: none;
margin: 0px;
}
> legend {
display: contents;
padding: 0px;
font-weight: bold;
line-height: var(--line-height);
// line-height: 5em;
&::after{
content: "";
display: block;
padding-bottom: 0.5em;
width: 100%;
}
}
}
// A PILL GROUP FOR GROUPED FORM INPUT ELEMENTS
// e.g. label, inputs, and buttons for a single conceptual group.
// Example:
// NOTE: the `className="w-36"` is entirely OPTIONAL, BUT, it allows
// for the labels of subsequent pill groups to be perfectly aligned.
//
// <fieldset className="pill-group">
// <legend><span className="w-36">Min Max</span></legend>
// <input type="number" id="minValInput" min="0" defaultValue="0" />
// <i className="separator"></i>
// <input type="number" id="maxValInput" min="1" defaultValue="1" />
// <button className={`${isBusy ? "busy" : ""}`} onClick={actBusy} disabled={isBusy}><i className="fa-solid fa-caret-right"></i></button>
// </fieldset>
//
fieldset.pill-group {
padding: 0px;
border: none;
width: 100%;
// background-color: orange;
display: flex;
flex-direction: row;
gap: 0px;
justify-content: flex-start;
align-items: stretch;
> legend {
all: unset; // Unset ALL styling for this pseudodo class.
display: contents;
> span {
display: flex;
// display: flex;
align-items: center; // Vertically centers text
justify-content: center;
padding: var(--padding-y) var(--padding-x);
font-weight: bold;
background-color: $themeDark1;
color: white;
line-height: var(--line-height);
}
&::after, &::before{
all: unset; // Unset ALL styling for this pseudodo class.
// content: "";
// padding-left: 1em;
// background-color: pink;
}
}
> input, select, > .input-bg {
flex-grow: 1;
}
> .separator {
width: 2px;
// background-color: $inputColorActive;
}
> * {
border-radius: 0;
}
> *:first-child, > legend:first-child > span {
border-top-left-radius: 1em;
border-bottom-left-radius: 1em;
}
> *:last-child {
border-top-right-radius: 1em;
border-bottom-right-radius: 1em;
}
// input-bg used as a container for other inputs that do not expand nicely
// on their own.
> .input-bg {
background-color: rgba(from $themeDark1 r g b / 0.5);
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
}
// Selection variant of the pill group.
// Designed specifically for displaying checkboxes and radio buttons as
// selectable pill segments.
// Example:
// // Example for checkboxes:
// <fieldset className="pill-select">
// <legend><span className="w-48">Favorite Desserts</span></legend>
// <label><input type="checkbox" name='check3a' />Jelly</label>
// <label><input type="checkbox" name='check3b' />Ice Cream</label>
// <label><input type="checkbox" name='check3c' />Flan</label>
// </fieldset>
//
// Example:
// // Example for radio buttons:
// <fieldset className="pill-select">
// <legend><span className="w-48">Favorite Dessert</span></legend>
// <label><input type="radio" name='favDesert5' value='jelly' />Jelly</label>
// <label><input type="radio" name='favDesert5' value='iceCream' />Ice Cream</label>
// <label><input type="radio" name='favDesert5' value='flan' />Flan</label>
// </fieldset>
fieldset.pill-select {
--bg-color: #{$inputColorBg};
--border-radius: #{$inputBorderRadius};
--select-text-color: white;
all: unset;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
gap: 2px; // the gap acts as a separator between the pills.
margin: 0px;
// width: 100%;
padding: 0;
border: none;
border-radius: var(--border-radius); // Creates the "pill" shape
overflow: hidden; // Ensures the labels don't spill over the rounded corners
// background-color: #f9f9f9;
background-color: transparent;
> legend {
all: unset; // Unset ALL styling for this pseudodo class.
display: contents;
> span {
display: flex;
// display: flex;
align-items: center; // Vertically centers text
justify-content: center;
padding: var(--padding-y) var(--padding-x);
font-weight: bold;
background-color: $themeDark1;
color: white;
line-height: var(--line-height);
}
&::after, &::before{
all: unset; // Unset ALL styling for this pseudodo class.
// content: "";
// padding-left: 1em;
// background-color: pink;
}
}
// Hide the actual radio input visually
& input[type="radio"], & input[type="checkbox"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
/* Individual labels as segments */
// & > label:has(> input[type=radio]:first-child) {
& > label:has(> input[type=radio]:first-child),
& > label:has(> input[type=checkbox]:first-child) {
// bookmark
all: unset;
margin-top: 0px;
margin: 0px;
flex: 1; // Makes all options take up equal width
text-align: center;
padding: var(--padding-y) var(--padding-x);
cursor: pointer;
background-color: var(--bg-color);
// background-color: orange;
// transition: background-color 80ms, color 80ms;
// border-right: 1px solid #f13737; // optionally add a colored separator.
// Remove the border from the last item
&:last-child {
border-right: none;
}
// Highlight the label containing the checked radio
&:has(input:checked) {
background-color: var(--focus-color); /* Primary highlight color */
color: var(--select-text-color);
}
// Hover effect for better UX
&:hover:not(:has(input:checked)) {
background-color: hsl(from var(--focus-color) h s calc(l + 20));
}
}
}
// Used as a file selector widget.
// Example:
// <fieldset className="file">
// <legend><span className="w-48">Profile Image</span></legend>
// <label>
// <div className="button" role="button "aria-role="button" aria-label="Select File"/>
// <input type="file" name="fileSelector2" id="fileSelector2" />
// </label>
// </fieldset>
fieldset.file {
--bg-color: #{$inputColorBg};
--border-radius: #{$inputBorderRadius};
--select-text-color: white;
all: unset;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
gap: 0px; // the gap acts as a separator between the pills.
margin: 0px;
// width: 100%;
padding: 0;
border: none;
border-radius: var(--border-radius); // Creates the "pill" shape
overflow: hidden; // Ensures the labels don't spill over the rounded corners
// background-color: #f9f9f9;
background-color: transparent;
// background-color: blue;
> legend {
all: unset; // Unset ALL styling for this pseudodo class.
display: contents;
> span {
display: flex;
// display: flex;
align-items: center; // Vertically centers text
justify-content: center;
padding: var(--padding-y) var(--padding-x);
font-weight: bold;
background-color: $themeDark1;
color: white;
line-height: var(--line-height);
}
&::after, &::before{
all: unset; // Unset ALL styling for this pseudodo class.
// content: "";
// padding-left: 1em;
// background-color: pink;
}
}
& label {
// display: block;
// font-size: 1em;
background-color: $inputColorBg;
display: flex;
flex-grow: 1;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: 0.5em;
// bookmark
& > .button {
// height: 100%;
// background-color: pink;
// content: "dfdsf";
--font-size: calc(var(--line-height) + var(--padding-y));
// --min-size: calc(var(--font-size) + var(--padding-y));
--min-size: calc(var(--font-size));
padding: 0.5em;
height: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
border-radius: 0px;
min-width: var(--min-size);
&::before {
// content: "\f15b"; // file icon
content: "\2b"; // plus icon
// content: "\f055"; // circle plus icon
font: var(--fa-font-solid);
font-size: calc(var(--line-height) + var(--padding-y)); // bookmark
font-weight: 900;
// background-color: pink;
}
}
// fieldset.file label::before
}
& input[type="file"] {
font-size: 1em;
flex-grow: 1;
width: 100%;
background-color: $inputColorBg;
}
& input[type="file"]::file-selector-button {
display: none;
}
}
// ------------------------------
// RANGE SLIDER INPUT FORM WIDGET
// ------------------------------
input[type="range"] {
--bg-color: #{$inputColorBg};
--thumb-color: #{$inputColorActive};
--slider-height: 1.6rem;
-webkit-appearance: none;
appearance: none;
// writing-mode: horizontal-tb;
direction: ltr;
background: transparent; // Background transparent, because color/styling will be done on the track pseudoclass.
height: var(--slider-height);
margin: 0;
// Styling the Thumb
// &::-webkit-slider-thumb, &::-moz-range-thumb {
&::-webkit-slider-thumb {
// For some reason Chrome does not like it when we define styles for BOTH
// webkit and mozilla thumb styles. in same block (even thugh they share
// same properties), we need to repeat the settingse separately.
// Chrome, Safari, Edge, Opera
-webkit-appearance: none; // Essential to allow custom styling
appearance: none;
height: var(--slider-height);
width: var(--slider-height);
border-radius: 50%;
border: none;
background-color: var(--thumb-color);
&:active {
// make thumb bigger when selected.
transform: scale(1.2);
}
}
&::-moz-range-thumb {
appearance: none;
height: var(--slider-height);
width: var(--slider-height);
border-radius: 50%;
border: none;
background-color: var(--thumb-color);
&:active {
// make thumb bigger when selected.
transform: scale(1.2);
}
}
// Styling the track
&::-webkit-slider-runnable-track {
background: var(--bg-color);
height: var(--slider-height);
border-radius: var(--slider-height);
}
// Firefox
&::-moz-range-track {
background: var(--bg-color);
height: var(--slider-height);
border-radius: var(--slider-height);
}
// Styling the thumb, when widget is focused.
&:focus {
outline: none;
// outline: 2px solid var(--thumb-color);
// outline-offset: 2px;
// border-radius: var(--slider-height);
&::-webkit-slider-thumb {
outline: 2px solid var(--thumb-color);
outline-offset: 2px;
border-radius: var(--slider-height);
}
&::-moz-range-thumb {
outline: 2px solid var(--thumb-color);
outline-offset: 2px;
border-radius: var(--slider-height);
}
}
}
// ----------------------
// FILE INPUT FORM WIDGET
// ----------------------
// NOTE: This is just the text portion of the file input widget.
// NOTE: See the buttons section for the styling of the file chooser button component.
input[type="file"] {
font-size: 1em;
// background-color: orange;
}
input[type="file"]::file-selector-button {
content: "ahooow";
}
// ----------------------
// BUTTONS
// ----------------------
button,
.button,
input[type="submit"],
input[type="button"],
input[type="file"]::file-selector-button
{
--color: white;
--bgColor: #{$buttonColorPrimary};
--bgColorHover: hsl(from var(--bgColor) h s calc(l - 10));
--bgColorClick: hsl(from var(--bgColor) h s calc(l - 15));
// Core flat button design properties
color: var(--color);
background-color: var(--bgColor);
border: none;
padding: 1rem 1.5rem;
text-decoration: none;
text-align: center;
display: inline-block;
font-size: 1.1rem;
cursor: pointer;
border-radius: 1rem;
}
// HOVER AND ACTIVE states specific to the file upload widget button.
// Needs to be handled separately because it interferes with the core button
// styles for the disabled state.
input[type="file"]::file-selector-button
{
&:hover {
background-color: var(--bgColorHover);
}
&:active {
background-color: var(--bgColorClick);
transform:scale(0.98);
}
}
// Core button styles.
button,
.button,
input[type="submit"],
input[type="button"]
{
// Button variant variable overrides.
&.primary { --color: white; --bgColor: #{$buttonColorPrimary};}
&.secondary { --color: white; --bgColor: #{$buttonColorSecondary};}
&.danger { --color: white; --bgColor: #{$buttonColorDanger};}
&.warning { --color: white; --bgColor: #{$buttonColorWarning};}
// HOVER AND ACTIVE states specific to the core button styles.
&:not(:disabled):hover {
background-color: var(--bgColorHover);
}
&:not(:disabled):active {
background-color: var(--bgColorClick);
transform:scale(0.98);
}
&:disabled {
opacity: 0.7;
cursor: not-allowed;
}
// Busy state styles
// Adds a loading spinner to the button.
&.busy {
color: rgba(from var(--color) r g b / 0.3);
padding-left: 2.25rem;
padding-right: 0.75rem;
}
&.busy::before {
content: "";
position: absolute;
width: 1em;
height: 1em;
margin-left: -1.5em; /* Offset for horizontal centering */
border: 2px solid rgba(from currentColor r g b / 0.3);
border-radius: 50%;
border-top-color: rgb(from currentColor r g b / 1);
animation: spin 0.8s linear infinite;
}
/* Rotation animation */
@keyframes spin {
to { transform: rotate(360deg); }
}
}
}
/* ========================================================================== */
/* BASIC QUALITY OF LIFE STYLES */
/* ========================================================================== */
@layer subutilities {
// // To be attached to any ul element that you DONT want bullets for.
.non-bullet-list {
list-style: none;
padding: 0;
margin: 0;
}
.side-by-side-list {
display: flex;
flex-direction: row;
list-style: none;
padding: 0;
margin: 0;
gap: 0;
}
}
@layer subutilities {
// VERTICAL STACK
.vstack {
display: flex;
flex-direction: column;
// gap: ;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: stretch;
}
// HORIZONTAL STACK
.hstack {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: stretch; // Default is stretch.
&.nowrap {
flex-wrap: nowrap;
}
&.stretch {
align-items: stretch;
}
&.baseline {
align-items: baseline;
}
&.top {
align-items: flex-start;
}
&.bottom {
align-items: flex-end;
}
&.vcenter {
align-items: center;
}
}
}
/* ========================================================================== */
/* PAGE NAVBAR MENU*/
/* ========================================================================== */
@media screen and (min-width: 62ch) {
#page-navbar #page-navbar-menu-button {
display: none;
color: pink;
}
}
@media screen and (max-width: 62ch) {
#page-navbar #page-navbar-menu-button {
/* display: block; */
display: inline-block;
cursor: pointer;
}
#page-navbar-menu-button:hover {
/* background-color: #82C13A; */
color: white;
}
:has(#page-navbar-menu-button[aria-expanded="true"]) #page-navbar ul {
display: unset;
}
:has(#page-navbar-menu-button[aria-expanded="false"]) #page-navbar ul {
display: none;
}
}
/* ========================================================================== */
/* PAGE NAVBAR */
/* ========================================================================== */
#page-navbar ul, #page-navbar li {
// Remove the default list styles.
all: unset;
}
#page-navbar {
position:absolute;
top: 0;
left: 0;
width: 100%;
line-height: 1.0;
padding: 0px;
border: 0;
margin: 0;
font-size: 1.2rem;
background-color: $themeDark1;
color: $themeGreen1;
}
#page-navbar ul {
/* display: block; */
list-style: none;
padding: 0;
margin: 0;
}
#page-navbar li {
/* display: inline-block; */
display: list-item;
float: left;
}
#page-navbar a, #page-navbar a:visited{
display: inline-block;
text-decoration: none;
text-align: center;
color: inherit;
padding: 1rem 1.5rem;
margin: 0px;
}
#page-navbar a:active {
color: #282a35;
}
#page-navbar li:hover {
background-color: $themeGreen1;
color: white;
}
#page-navbar li.navbar-right {
float: right;
}
@media screen and (max-width: 62ch) {
#page-navbar ul li.navbar-right,
#page-navbar ul li {
float: none;
}
#page-navbar-links-list a {
width: 100%;
text-align: left;
}
}
/* ========================================================================== */
/* PAGE FOOTER */
/* ========================================================================== */
#page-footer {
/* position: absolute; */
bottom: 0;
left: 0;
width: 100%;
padding: 3rem 1rem;
border: 0;
/* margin: 0; */
/* margin-top: auto; */
background-color: $themeDark1;
color: $themeGreen1;
text-align: center;
}
/* ========================================================================== */
/* ERRORS */
/* ========================================================================== */
.error-message-container {
color: $themeRed1;
// lighten the background color by N amount relative to the theme red color, but,
// clip to a maximum lightness of 90.
background-color: hsl(from $themeRed1 h s min(calc(l + 30), 90));
font-size: 1rem;
font-weight: 500;
text-align: center;
margin: 1rem 0;
width: 100%;
padding: 1.5em 1.5em;
}