/// ===== Root =====
/// Every HTML document using Bluelib must define a root, an element where all bluelib styles can start being applied.
/// Elements outside the root won't be styled at all, allowing Bluelib's styling to be contained in components or modules of a website.
@{bluelib} {
// Set the background defined in the palette
background-color: @hex-background;
// Set the base color to the foreground defined in the palette
.bluelib-color(@hex-foreground);
// By default, text should use the main font
.font-text();
/// ===== All =====
/// By default, browsers style elements with some weird rules.
/// This is an attempt to correct them, and to implement some Bluelib magic.
&, @{all} {
// Globally set the box-sizing to border-box, as content-box is completely insane
&, &::before, &::after {
box-sizing: border-box;
}
// Bluelib magic!
// This rule sets the color of all text to be equal to the current .bluelib-color() at 100% opacity.
// Thanks, CSS variables!
color: @bA;
}
/// ===== Layouts =====
/// Layouts arrange the content displayed inside the root.
/// Layouts are the second element in the Bluelib hierarchy, immediately following the root.
/// Ideally, a single layout should be used per page.
// The base layout class.
@{layout} {
display: grid;
justify-content: stretch;
align-items: stretch;
width: 100%;
}
// A layout with a single container spanning the whole screen.
@{layout-fill} {
padding: 4px;
grid-template-areas:
"single";
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
@{layout-fill-single} {
grid-area: single;
}
// A layout with a centered 1024px wide main container and two symmetric containers at the sides.
@{layout-threecol} {
padding: 4px;
@media screen and (min-width: 1281px) {
grid-template-areas:
"left center right";
grid-template-columns: 1fr auto 1fr;
grid-template-rows: 1fr;
}
// If the side containers would be smaller than 128px (if the screen is narrower than 1280px), the smaller containers are moved below the main one.
@media screen and (max-width: 1280px) {
grid-template-areas:
"center center"
"left right";
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
// Keep the container limited to 1024px and centered
max-width: 1024px;
margin-left: auto;
margin-right: auto;
}
}
@{layout-threecol-left} {
grid-area: left;
@media screen and (min-width: 1281px) {
justify-self: end;
}
@media screen and (max-width: 1280px) {
justify-self: stretch;
}
}
@{layout-threecol-center} {
grid-area: center;
// Ensure the container doesn't eat up all the space
max-width: 1024px;
}
@{layout-threecol-right} {
grid-area: right;
@media screen and (min-width: 1281px) {
justify-self: start;
}
@media screen and (max-width: 1280px) {
justify-self: stretch;
}
}
/// ===== Panels =====
/// Panels are the main sectioning elements available in Bluelib.
/// Many instance of them should be present on the page!
/// They should group small amounts of tightly related content, which can be understood more or less separately from the rest of the page.
// The base panel class.
@{panel} {
// Panels have a margin to separate them from other elements
margin: 8px 0;
// And a padding to separate them from their contents
padding: 8px;
// Panels should try to fill the most space possible
width: 100%;
background-color: @b0;
border-color: @b1;
// The first and last elements of a panel should not have a margin
> @{all} {
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
// A panel which encloses its contents with a border.
@{panel-box} {
border-radius: 4px;
border-width: 2px;
border-style: solid;
}
// A panel which encloses its contents with a quote-like border.
@{panel-blockquote} {
border-radius: 4px;
border-width: 0 0 0 2px;
border-style: solid;
}
// A panel with no border whose contents are made smaller.
@{panel-aside} {
border-width: 0;
font-size: smaller;
}
/// ===== Split =====
/// A split is an element which splits everything contained inside in multiple columns of equal width.
@{split} {
display: flex;
flex-wrap: wrap;
gap: 4px 8px;
justify-content: center;
> @{panel} {
// Add a horizontal margin to panels
margin: 0;
}
// Allow contained elements to grow
> @{all} {
flex-grow: 1;
flex-shrink: 0;
flex-basis: 0;
}
}
// Force a split to wrap
@{split-forcewrap} {
flex-grow: 0;
flex-shrink: 0;
flex-basis: 100%;
}
/// ===== Separators =====
/// Separators are horizontal lines separating elements.
@{separator} {
border-width: 1px;
border-style: solid;
border-color: @b5;
}
@{separator-light} {
border-color: @b1;
}
@{separator-heavy} {
border-color: @bA;
}
/// --- Images ---
/// Images are changed to block elements and are automatically centered.
@{image} {
display: block;
max-width: 100%;
margin-left: auto;
margin-right: auto;
object-fit: contain;
}
// Allow an image to grow to its real size
@{image-free} {
max-width: unset;
max-height: unset;
}
// Limit even further the size of an image based on the size of the screen
@{image-limited} {
max-height: max(37.5vw, 66.6vh);
}
/// ===== Forms =====
/// Forms are containers where interactable elements are contained.
/// Every interactable is split in two parts: a label on the left and the control on the right
///
/// Example:
///
/// Username | Steffo
/// Email | ste.pigozzi@gmail.com
/// Password | ············
/// Country | Italy ↓
///
/// [Submit]
@{form} {
display: grid;
align-items: center;
row-gap: 4px;
column-gap: 8px;
// Labels should have the greater width between the one they require and 1/6 of the form width.
grid-template-columns: minmax(auto, 1fr) 5fr;
}
@{form-label} {
// Labels are on the left column of the grid
grid-column: 1;
// They should be right-aligned
justify-self: end;
text-align: right;
}
@{form-input} {
// Inputs are on the right column of the grid
grid-column: 2;
// They should occupy all the available space
justify-self: stretch;
width: 100%;
}
// An element filling a whole row of a form
// Useful for adding buttons or similar flow interruptions
@{form-row} {
grid-column-start: 1;
grid-column-end: 3;
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
width: 100%;
margin-top: 8px;
margin-bottom: 8px;
}
/// ===== Buttons =====
/// Buttons are clickable elements which should trigger an action when clicked.
/// Their contents are elements containing either an icon or text, or possibly both.
@{button} {
// Buttons are rendered as inline elements, but should arrange their contents as if they were flex
display: inline-flex;
// Center the button contents both vertically and horizontally
justify-content: center;
align-items: center;
// Disable the operating system appearance for the button
appearance: none;
// Button properties should be set explicitly, as otherwise every browser will display them in a different manner
background-color: @bA;
color: @hex-away;
border: none;
padding: 6px 16px;
border-radius: 4px;
font-family: inherit;
font-size: inherit;
// Buttons should display a pointer cursor
cursor: pointer;
&:hover {
// Hovered buttons should provide feedback
background-color: @bC;
}
&:active {
// Activated buttons should provide greater feedback
background-color: @bF;
}
// Disabled buttons shouldn't do anything
&@{status-disabled} {
&:hover {
background-color: @bA;
}
&:active {
background-color: @bA;
}
}
// If using keyboard navigation, display an outline to make the focused button more evident
&:focus-visible {
outline: 4px solid @b7 !important;
}
}
@{button-fill-width} {
width: 100%;
}
@{button-toggle} {
padding: 4px 14px;
border-width: 2px;
border-style: solid;
&@{status-disabled} {
border-style: dashed;
}
}
@{button-toggle-off} {
border-color: @bA;
color: @bA;
background-color: @b0;
&:hover {
border-color: @bC;
color: @bC;
background-color: @b1;
}
&:active {
border-color: @bF;
color: @bF;
background-color: @b2;
}
&@{status-disabled} {
border-style: dashed;
&:hover {
border-color: @bA;
color: @bA;
background-color: @b0;
}
&:active {
border-color: @bA;
color: @bA;
background-color: @b0;
}
}
}
@{button-toggle-on} {
border-color: @bF;
color: @bF;
background-color: @b2;
&:hover {
border-color: @bC;
color: @bC;
background-color: @b1;
}
&:active {
border-color: @bA;
color: @bA;
background-color: @b0;
}
&@{status-disabled} {
border-style: dashed;
&:hover {
border-color: @bF;
color: @bF;
background-color: @b2;
}
&:active {
border-color: @bF;
color: @bF;
background-color: @b2;
}
}
}
// --- Inputs ---
@{input} {
border-style: solid;
border-color: @b3;
color: @bA;
background-color: @b0;
font-family: inherit;
font-size: inherit;
&::placeholder {
color: @b4;
opacity: 1;
}
&:optional, &:optional::placeholder {
font-style: italic;
}
// &:valid:not(:placeholder-shown) {
// .bluelib-color(@hex-lime);
// }
&:invalid:not(:placeholder-shown) {
.bluelib-color(@hex-red);
}
&:hover, &:focus {
border-color: @b6;
color: @bF;
background-color: @b1;
&::placeholder {
color: @b7;
}
}
&@{status-disabled} {
border-style: dashed;
&:hover, &:focus {
border-color: @b3;
color: @bA;
background-color: @b0;
&::placeholder {
color: @b4;
}
}
}
}
@{input-field} {
cursor: text;
border-width: 0 0 2px 0;
border-radius: 4px 4px 0 0;
padding: 4px 6px;
}
@{input-select}, @{input-multiselect} {
// Browsers add 4px of unremovable horizontal padding to options for some reason
padding: 4px 2px;
}
@{input-select-option}, @{input-multiselect-option} {
font-weight: normal;
color: @bA;
padding: 4px 4px;
// TODO: style checked elements, when it will be possible
}
@{input-select-optgroup}, @{input-multiselect-optgroup} {
font-weight: bold;
color: @hex-accent;
padding: 4px 4px;
@{input-select-option}, @{input-multiselect-option} {
padding: 4px 20px;
&::before {
content: "";
}
}
}
@{input-select-optgroup}, @{input-select-option}, @{input-multiselect-optgroup}, @{input-multiselect-option} {
font-family: inherit;
font-size: inherit;
font-style: inherit;
}
@{input-select} {
cursor: context-menu;
border-width: 0 0 2px 0;
border-radius: 4px 4px 0 0;
@media screen and (-webkit-min-device-pixel-ratio:0) {
@{input-select-option}, @{input-select-optgroup} {
background-color: @hex-background;
}
}
}
@{input-multiselect} {
cursor: vertical-text;
border-width: 0 0 0 2px;
border-radius: 0 4px 4px 0;
}
// --- Element status ---
@{status-hoverable} {
cursor: help;
}
@{status-clickable} {
cursor: pointer;
}
@{status-disabled} {
cursor: not-allowed !important;
opacity: 50%;
}
// --- Elements ---
@{element-title} {
text-align: center;
.bluelib-color(@hex-accent);
.font-title();
}
@{element-paragraph} {
}
@{element-list-item} {
margin: 10px 0;
}
@{element-anchor} {
.bluelib-color(@hex-link);
text-decoration-line: underline;
text-decoration-style: solid;
text-decoration-color: currentColor;
text-decoration-thickness: 1px;
&:hover, &:focus {
.bluelib-color(@hex-link-hover);
}
&:focus-visible {
text-decoration-thickness: 2px;
}
&:active {
.bluelib-color(@hex-link-active);
}
&@{status-disabled} {
text-decoration-style: dashed;
&:hover, &:focus {
.bluelib-color(@hex-link);
}
&:active {
.bluelib-color(@hex-link);
}
}
}
// --- Alignment ---
@{align-left} {
text-align: left;
}
@{align-center} {
text-align: center;
}
@{align-right} {
text-align: right;
}
// --- Sizes ---
@{size-xxl} {
font-size: xx-large;
}
@{size-xl} {
font-size: x-large;
}
@{size-l} {
font-size: large;
}
@{size-m} {
font-size: medium;
}
@{size-s} {
font-size: small;
}
@{size-xs} {
font-size: x-small;
}
@{size-xxs} {
font-size: xx-small;
}
// --- Styles ---
@{style-bold} {
font-weight: bold;
.bluelib-color(@hex-accent);
}
@{style-italic} {
font-style: italic;
}
@{style-underline} {
text-decoration-line: underline;
}
@{style-strike} {
text-decoration-line: line-through;
}
@{style-monospace} {
.font-code();
}
@{style-keyboard} {
.font-code();
padding: 2px 4px;
border: 2px outset;
border-radius: 4px;
}
// --- Colors ---
@{color-red} {
.bluelib-color(@hex-red)
}
@{color-orange} {
.bluelib-color(@hex-orange)
}
@{color-yellow} {
.bluelib-color(@hex-yellow)
}
@{color-lime} {
.bluelib-color(@hex-lime)
}
@{color-cyan} {
.bluelib-color(@hex-cyan)
}
@{color-blue} {
.bluelib-color(@hex-blue)
}
@{color-magenta} {
.bluelib-color(@hex-magenta)
}
@{color-gray} {
.bluelib-color(@hex-gray)
}
}