Fiddling around more with login

This commit is contained in:
Z. Charles Dziura 2025-07-11 20:16:37 -04:00
parent d0c9d851f6
commit 16bac8e9cf
20 changed files with 602 additions and 224 deletions

View file

@ -15,6 +15,7 @@
"@angular/platform-browser": "^20.0.0",
"@angular/router": "^20.0.0",
"@ngrx/signals": "^19.2.1",
"@picocss/pico": "^2.1.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"uuid": "^11.1.0"
@ -2816,6 +2817,12 @@
"license": "MIT",
"optional": true
},
"node_modules/@picocss/pico": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@picocss/pico/-/pico-2.1.1.tgz",
"integrity": "sha512-kIDugA7Ps4U+2BHxiNHmvgPIQDWPDU4IeU6TNRdvXQM1uZX+FibqDQT2xUOnnO2yq/LUHcwnGlu1hvf4KfXnMg==",
"license": "MIT"
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",

View file

@ -17,6 +17,7 @@
"@angular/platform-browser": "^20.0.0",
"@angular/router": "^20.0.0",
"@ngrx/signals": "^19.2.1",
"@picocss/pico": "^2.1.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"uuid": "^11.1.0"

View file

@ -1,11 +0,0 @@
<header>
<ng-content select=".card-header"></ng-content>
</header>
<div class="content">
<ng-content></ng-content>
</div>
<footer>
<ng-content select=".card-footer"></ng-content>
</footer>

View file

@ -1,22 +0,0 @@
:host {
border: 1px solid var(--color-outline);
border-radius: 1.2rem;
color: var(--color-on-surface);
display: inline-grid;
font-size: 1.8rem;
grid-template-areas:
'head'
'body'
'foot';
grid-template-rows: min-content auto min-content;
padding: 1.6rem;
}
header {
margin-top: 0.4rem;
}
footer {
justify-content: flex-end;
display: flex;
flex-direction: row;
margin-top: 1.2rem;
}

View file

@ -1,22 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Card } from './card';
describe('Card', () => {
let component: Card;
let fixture: ComponentFixture<Card>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [Card],
}).compileComponents();
fixture = TestBed.createComponent(Card);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,8 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-card',
templateUrl: './card.html',
styleUrl: './card.scss',
})
export class Card {}

View file

@ -1,8 +1,16 @@
<app-card>
<h3 class="card-header">Login</h3>
<div>
<form [formGroup]="form">
</form>
</div>
</app-card>
<h1>Aisle17</h1>
<form [formGroup]="form" (submit)="onSubmit()">
<fieldset>
<label>
Email address
<input type="email" autocomplete="email" [attr.aria-invalid]="isEmailInvalid()" formControlName="email">
</label>
<label>
Password
<input type="password" autocomplete="current-password" [attr.aria-invalid]="isPasswordInvalid()" formControlName="password">
</label>
<input type="submit" value="Log in" [disabled]="!isFormValid()">
</fieldset>
</form>
<hr>
<input type="button" value="Register">

View file

@ -1,3 +1,10 @@
:host {
display: grid;
}
form {
fieldset,
input[type='submit'] {
margin-bottom: 0;
}
}

View file

@ -1,16 +1,47 @@
import { Component, inject } from '@angular/core';
import { Component, inject, Signal } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { Card } from 'components/card/card';
import { debounceTime, map } from 'rxjs';
@Component({
selector: 'app-login',
templateUrl: './login.html',
styleUrl: './login.scss',
imports: [Card, FormsModule, ReactiveFormsModule],
imports: [FormsModule, ReactiveFormsModule],
})
export class Login {
readonly form = inject(FormBuilder).group({
username: ['', Validators.required],
password: ['', Validators.required],
email: ['', Validators.compose([Validators.required, Validators.email])],
password: ['', Validators.compose([Validators.required, Validators.minLength(8)])],
});
private get email() {
return this.form.get('email')!;
}
private get password() {
return this.form.get('password')!;
}
readonly isEmailInvalid: Signal<boolean> = toSignal(
this.email.statusChanges.pipe(
debounceTime(500),
map(status => this.email.dirty && status !== 'VALID')
),
{ initialValue: false }
);
readonly isPasswordInvalid: Signal<boolean> = toSignal(
this.password.statusChanges.pipe(
debounceTime(500),
map(status => this.email.dirty && status !== 'VALID')
),
{ initialValue: false }
);
readonly isFormValid: Signal<boolean> = toSignal(this.form.statusChanges.pipe(map(status => status === 'VALID')), {
initialValue: this.form.dirty && this.form.status === 'VALID',
});
onSubmit() {}
}

View file

@ -1,13 +1,5 @@
<app-card>
<h2 class="card-header">{{ name }}</h2>
@if (foods!!.length > 0) {
@if (foods!!.length > 0) {
} @else {
No foods planned. Click the button below to add some!
}
<div class="card-footer">
<button class="primary">Add Food</button>
</div>
</app-card>
} @else {
No foods planned. Click the button below to add some!
}

View file

@ -1,10 +1,8 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { IFood, MealType } from 'models/meal.model';
import { Card } from 'components/card/card';
@Component({
selector: 'app-meal',
imports: [Card],
templateUrl: './meal.html',
styleUrl: './meal.scss',
changeDetection: ChangeDetectionStrategy.OnPush,

View file

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8">
<title>Webapp</title>

View file

@ -1,45 +1,25 @@
@import url('./styles/reset.scss');
@import url('./styles/typography.scss');
@import url('./styles/components/button.scss');
@use '@picocss/pico/scss' with (
$enable-semantic-container: true,
$enable-classes: false,
$modules: (
// Theme
'themes/default': false,
:root {
--color-primary: oklch(0.327 0.057852 184.2083);
--color-on-primary: white;
--color-primary-container: oklch(0.5247 0.0849 184.3);
--color-primary-container-alt: oklch(from var(--color-primary-container) 0.565 c h);
--color-on-primary-container: white;
// Layout
'layout/grid': false,
--color-secondary: oklch(0.3394 0.0927 273.22);
--color-on-secondary: white;
--color-secondary-container: oklch(0.5379 0.0899 276.11);
--color-on-secondary-container: white;
// Content
'content/code': false,
--color-tertiary: oklch(0.3384 0.0788 60.74);
--color-on-tertiary: white;
--color-tertiary-container: oklch(0.5416 0.1017 61.52);
--color-on-tertiary-container: white;
// Forms
'forms/input-color': false,
'forms/input-file': false,
)
);
--color-error: oklch(0.3413 0.087 34.27);
--color-on-error: white;
--color-error-container: oklch(0.5441 0.0989 34.26);
--color-on-error-container: white;
--color-surface: oklch(0.9816 0.0055 211.04);
--color-on-surface: oklch(0.1764 0.0095 208.78);
--color-surface-container: oklch(0.9297 0.0066 208.78);
--color-outline: oklch(0.4386 0.0119 212.57);
--color-outline-variant: oklch(0.5336 0.0123 204.04);
--color-shadow: oklch(0.3396 0.0743 281.7 / 25%);
--color-shadow-variant: oklch(0 0 0 / 30%);
--box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
font-family: system-ui, sans-serif;
font-size: 62.5%;
font-weight: 400;
}
// Custom Theme
@use 'styles/theme/styles';
@use 'styles/theme/schemes';
body {
margin: 0;

View file

@ -1,23 +0,0 @@
button {
background-color: var(--color-surface-container);
border: 1px solid var(--color-outline);
border-radius: 0.4rem;
color: var(--color-on-surface-container);
cursor: pointer;
font-size: 0.8rem;
padding: 0.2rem 0.6rem;
&:hover {
box-shadow: var(--color-shadow) 0px 3px 8px;
box-shadow: var(--color-shadow) 0px 2px 5px -1px, var(--color-shadow-variant) 0px 1px 3px -1px;
&:active {
background-color: var(--color-primary-container-alt);
}
}
&.primary {
background-color: var(--color-primary-container);
color: var(--color-on-primary-container);
}
}

View file

@ -1,54 +0,0 @@
/* 1. Use a more-intuitive box-sizing model */
*, *::before, *::after {
box-sizing: border-box;
}
/* 2. Remove default margin */
* {
margin: 0;
}
/* 3. Enable keyword animations */
@media (prefers-reduced-motion: no-preference) {
html {
interpolate-size: allow-keywords;
}
}
body {
/* 4. Add accessible line-height */
line-height: 1.5;
/* 5. Improve text rendering */
-webkit-font-smoothing: antialiased;
}
/* 6. Improve media defaults */
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
/* 7. Inherit fonts for form controls */
input, button, textarea, select {
font: inherit;
}
/* 8. Avoid text overflows */
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
/* 9. Improve line wrapping */
p {
text-wrap: pretty;
}
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
}
/*
10. Create a root stacking context
*/
#root, #__next {
isolation: isolate;
}

View file

@ -0,0 +1,144 @@
@use 'sass:color';
@use 'sass:map';
@use '@picocss/pico/scss/settings' as *;
@use '@picocss/pico/scss/colors' as *;
@use '@picocss/pico/scss/helpers/functions';
$theme-color: #0074d9;
// Default: Dark theme
@mixin theme {
#{$css-var-prefix}background-color: #{color.mix($slate-950, $slate-900)};
// Text color
#{$css-var-prefix}color: #{$zinc-100};
// Text selection color
#{$css-var-prefix}text-selection-color: #{rgba($theme-color, 0.1875)};
// Muted colors
#{$css-var-prefix}muted-color: #{$zinc-450};
#{$css-var-prefix}muted-border-color: #{$slate-850};
// Primary colors
#{$css-var-prefix}primary: #{$theme-color};
#{$css-var-prefix}primary-background: var(#{$css-var-prefix}primary);
#{$css-var-prefix}primary-border: var(#{$css-var-prefix}primary-background);
#{$css-var-prefix}primary-underline: #{rgba($theme-color, 0.5)};
#{$css-var-prefix}primary-hover: #{color.scale($theme-color, $lightness: 21%)};
#{$css-var-prefix}primary-hover-background: var(#{$css-var-prefix}primary-hover);
#{$css-var-prefix}primary-hover-border: var(#{$css-var-prefix}primary-hover-background);
#{$css-var-prefix}primary-hover-underline: var(#{$css-var-prefix}primary-hover);
#{$css-var-prefix}primary-focus: #{rgba($theme-color, 0.375)};
#{$css-var-prefix}primary-inverse: #{$black};
// Secondary colors
#{$css-var-prefix}secondary: #{$zinc-350};
#{$css-var-prefix}secondary-background: #{$slate-600};
#{$css-var-prefix}secondary-border: var(#{$css-var-prefix}secondary-background);
#{$css-var-prefix}secondary-underline: #{rgba($zinc-350, 0.5)};
#{$css-var-prefix}secondary-hover: #{$zinc-250};
#{$css-var-prefix}secondary-hover-background: #{$slate-550};
#{$css-var-prefix}secondary-hover-border: var(#{$css-var-prefix}secondary-hover-background);
#{$css-var-prefix}secondary-hover-underline: var(#{$css-var-prefix}secondary-hover);
#{$css-var-prefix}secondary-focus: #{rgba($slate-350, 0.25)};
#{$css-var-prefix}secondary-inverse: #{$white};
// Contrast colors
#{$css-var-prefix}contrast: #{$slate-100};
#{$css-var-prefix}contrast-background: #{$slate-50};
#{$css-var-prefix}contrast-border: var(#{$css-var-prefix}contrast-background);
#{$css-var-prefix}contrast-underline: #{rgba($slate-100, 0.5)};
#{$css-var-prefix}contrast-hover: #{$white};
#{$css-var-prefix}contrast-hover-background: #{$white};
#{$css-var-prefix}contrast-hover-border: var(#{$css-var-prefix}contrast-hover-background);
#{$css-var-prefix}contrast-hover-underline: var(#{$css-var-prefix}contrast-hover);
#{$css-var-prefix}contrast-focus: #{rgba($slate-150, 0.25)};
#{$css-var-prefix}contrast-inverse: #{$black};
// Box shadow
#{$css-var-prefix}box-shadow: functions.shadow($black);
// Typography
@if map.get($modules, 'content/typography') {
// Headings colors
#{$css-var-prefix}h1-color: #{$zinc-50};
#{$css-var-prefix}h2-color: #{$zinc-100};
#{$css-var-prefix}h3-color: #{$zinc-200};
#{$css-var-prefix}h4-color: #{$zinc-250};
#{$css-var-prefix}h5-color: #{$zinc-300};
#{$css-var-prefix}h6-color: #{$zinc-400};
// Highlighted text (<mark>)
#{$css-var-prefix}mark-background-color: #{$azure-750};
#{$css-var-prefix}mark-color: #{$white};
// Inserted (<ins>) & Deleted (<del>)
#{$css-var-prefix}ins-color: #{color.mix($jade-450, $zinc-200)};
#{$css-var-prefix}del-color: #{color.mix($red-500, $zinc-200)};
// Blockquote
#{$css-var-prefix}blockquote-border-color: var(#{$css-var-prefix}muted-border-color);
#{$css-var-prefix}blockquote-footer-color: var(#{$css-var-prefix}muted-color);
}
// Button
@if map.get($modules, 'content/button') {
// To disable box-shadow, remove the var or set to '0 0 0 rgba(0, 0, 0, 0)'
// Don't use, 'none, 'false, 'null', '0', etc.
#{$css-var-prefix}button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
#{$css-var-prefix}button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
}
// Table
@if map.get($modules, 'content/table') {
#{$css-var-prefix}table-border-color: var(#{$css-var-prefix}muted-border-color);
#{$css-var-prefix}table-row-stripped-background-color: #{rgba($zinc-500, 0.0375)};
}
// Form elements
@if map.get($modules, 'forms/basics') {
#{$css-var-prefix}form-element-background-color: #{color.mix($slate-900, $slate-850)};
#{$css-var-prefix}form-element-selected-background-color: #{$slate-800};
#{$css-var-prefix}form-element-border-color: #{$slate-800};
#{$css-var-prefix}form-element-color: #{$zinc-100};
#{$css-var-prefix}form-element-placeholder-color: #{$zinc-400};
#{$css-var-prefix}form-element-active-background-color: #{color.mix($slate-900, $slate-850, 75%)};
#{$css-var-prefix}form-element-active-border-color: var(#{$css-var-prefix}primary-border);
#{$css-var-prefix}form-element-focus-color: var(#{$css-var-prefix}primary-focus);
#{$css-var-prefix}form-element-disabled-background-color: var(#{$css-var-prefix}form-element-background-color);
#{$css-var-prefix}form-element-disabled-border-color: var(#{$css-var-prefix}form-element-border-color);
#{$css-var-prefix}form-element-disabled-opacity: 0.5;
#{$css-var-prefix}form-element-invalid-border-color: #{color.mix($red-500, $slate-600)};
#{$css-var-prefix}form-element-invalid-active-border-color: #{color.mix($red-500, $slate-600, 75%)};
#{$css-var-prefix}form-element-invalid-focus-color: var(
#{$css-var-prefix}form-element-invalid-active-border-color
);
#{$css-var-prefix}form-element-valid-border-color: #{color.mix($jade-450, $slate-600)};
#{$css-var-prefix}form-element-valid-active-border-color: #{color.mix($jade-450, $slate-600, 75%)};
#{$css-var-prefix}form-element-valid-focus-color: var(#{$css-var-prefix}form-element-valid-active-border-color);
// Focus for buttons, radio and select
input:is([type='submit'], [type='button'], [type='reset'], [type='checkbox'], [type='radio'], [type='file']) {
#{$css-var-prefix}form-element-focus-color: var(#{$css-var-prefix}primary-focus);
}
}
// Range (input[type="range"])
@if map.get($modules, 'forms/input-range') {
#{$css-var-prefix}range-border-color: #{$slate-850};
#{$css-var-prefix}range-active-border-color: #{$slate-800};
#{$css-var-prefix}range-thumb-border-color: var(#{$css-var-prefix}background-color);
#{$css-var-prefix}range-thumb-color: var(#{$css-var-prefix}primary-background);
#{$css-var-prefix}range-thumb-active-color: var(#{$css-var-prefix}primary-hover-background);
}
// Form validation icons
@if map.get($modules, 'forms/basics') {
#{$css-var-prefix}icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{functions.display-rgb(color.mix($jade-450, $slate-600))}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
#{$css-var-prefix}icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{functions.display-rgb(color.mix($red-500, $slate-600))}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
}
// Document
color-scheme: dark;
}

View file

@ -0,0 +1,144 @@
@use 'sass:color';
@use 'sass:map';
@use '@picocss/pico/scss/settings' as *;
@use '@picocss/pico/scss/colors' as *;
@use '@picocss/pico/scss/helpers/functions';
$theme-color: #001f3f;
// Default: Light theme
@mixin theme {
#{$css-var-prefix}background-color: #{$white};
// Text color
#{$css-var-prefix}color: #{$zinc-900};
// Text selection color
#{$css-var-prefix}text-selection-color: #{rgba($theme-color, 0.25)};
// Muted colors
#{$css-var-prefix}muted-color: #{$zinc-550};
#{$css-var-prefix}muted-border-color: #{color.mix($slate-100, $slate-50)};
// Primary colors
#{$css-var-prefix}primary: #{$theme-color};
#{$css-var-prefix}primary-background: var(#{$css-var-prefix}primary);
#{$css-var-prefix}primary-border: var(#{$css-var-prefix}primary-background);
#{$css-var-prefix}primary-underline: #{rgba($theme-color, 0.5)};
#{$css-var-prefix}primary-hover: #{color.scale($theme-color, $lightness: -21%)};
#{$css-var-prefix}primary-hover-background: #{color.scale($theme-color, $lightness: 21%)};
#{$css-var-prefix}primary-hover-border: var(#{$css-var-prefix}primary-hover-background);
#{$css-var-prefix}primary-hover-underline: var(#{$css-var-prefix}primary-hover);
#{$css-var-prefix}primary-focus: #{rgba($theme-color, 0.375)};
#{$css-var-prefix}primary-inverse: #{$black};
// Secondary colors
#{$css-var-prefix}secondary: #{$slate-550};
#{$css-var-prefix}secondary-background: #{$slate-600};
#{$css-var-prefix}secondary-border: var(#{$css-var-prefix}secondary-background);
#{$css-var-prefix}secondary-underline: #{rgba($slate-550, 0.5)};
#{$css-var-prefix}secondary-hover: #{$slate-650};
#{$css-var-prefix}secondary-hover-background: #{$slate-650};
#{$css-var-prefix}secondary-hover-border: var(#{$css-var-prefix}secondary-hover-background);
#{$css-var-prefix}secondary-hover-underline: var(#{$css-var-prefix}secondary-hover);
#{$css-var-prefix}secondary-focus: #{rgba($slate-550, 0.25)};
#{$css-var-prefix}secondary-inverse: #{$white};
// Contrast colors
#{$css-var-prefix}contrast: #{$slate-900};
#{$css-var-prefix}contrast-background: #{$slate-900};
#{$css-var-prefix}contrast-border: var(#{$css-var-prefix}contrast-background);
#{$css-var-prefix}contrast-underline: #{rgba($slate-900, 0.5)};
#{$css-var-prefix}contrast-hover: #{$black};
#{$css-var-prefix}contrast-hover-background: #{$black};
#{$css-var-prefix}contrast-hover-border: var(#{$css-var-prefix}contrast-hover-background);
#{$css-var-prefix}contrast-hover-underline: var(#{$css-var-prefix}secondary-hover);
#{$css-var-prefix}contrast-focus: #{rgba($slate-550, 0.25)};
#{$css-var-prefix}contrast-inverse: #{$white};
// Box shadow
#{$css-var-prefix}box-shadow: functions.shadow($slate-400);
// Typography
@if map.get($modules, 'content/typography') {
// Headings colors
#{$css-var-prefix}h1-color: #{$zinc-800};
#{$css-var-prefix}h2-color: #{$zinc-750};
#{$css-var-prefix}h3-color: #{$zinc-700};
#{$css-var-prefix}h4-color: #{$zinc-650};
#{$css-var-prefix}h5-color: #{$zinc-600};
#{$css-var-prefix}h6-color: #{$zinc-550};
// Highlighted text (<mark>)
#{$css-var-prefix}mark-background-color: #{color.mix($amber-100, $amber-50)};
#{$css-var-prefix}mark-color: #{$zinc-950};
// Inserted (<ins>) & Deleted (<del>)
#{$css-var-prefix}ins-color: #{color.mix($jade-450, $zinc-750)};
#{$css-var-prefix}del-color: #{color.mix($red-500, $zinc-750)};
// Blockquote
#{$css-var-prefix}blockquote-border-color: var(#{$css-var-prefix}muted-border-color);
#{$css-var-prefix}blockquote-footer-color: var(#{$css-var-prefix}muted-color);
}
// Button
@if map.get($modules, 'content/button') {
// To disable box-shadow, remove the var or set to '0 0 0 rgba(0, 0, 0, 0)'
// Don't use, 'none, 'false, 'null', '0', etc.
#{$css-var-prefix}button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
#{$css-var-prefix}button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
}
// Table
@if map.get($modules, 'content/table') {
#{$css-var-prefix}table-border-color: var(#{$css-var-prefix}muted-border-color);
#{$css-var-prefix}table-row-stripped-background-color: #{rgba($zinc-500, 0.0375)};
}
// Form elements
@if map.get($modules, 'forms/basics') {
#{$css-var-prefix}form-element-background-color: #{color.mix($slate-50, $white, 25%)};
#{$css-var-prefix}form-element-selected-background-color: #{$slate-100};
#{$css-var-prefix}form-element-border-color: #{$slate-150};
#{$css-var-prefix}form-element-color: #{$zinc-850};
#{$css-var-prefix}form-element-placeholder-color: var(#{$css-var-prefix}muted-color);
#{$css-var-prefix}form-element-active-background-color: #{$white};
#{$css-var-prefix}form-element-active-border-color: var(#{$css-var-prefix}primary-border);
#{$css-var-prefix}form-element-focus-color: var(#{$css-var-prefix}primary-focus);
#{$css-var-prefix}form-element-disabled-background-color: var(#{$css-var-prefix}form-element-background-color);
#{$css-var-prefix}form-element-disabled-border-color: var(#{$css-var-prefix}form-element-border-color);
#{$css-var-prefix}form-element-disabled-opacity: 0.5;
#{$css-var-prefix}form-element-invalid-border-color: #{color.mix($red-500, $zinc-350)};
#{$css-var-prefix}form-element-invalid-active-border-color: #{color.mix($red-500, $zinc-350, 75%)};
#{$css-var-prefix}form-element-invalid-focus-color: var(
#{$css-var-prefix}form-element-invalid-active-border-color
);
#{$css-var-prefix}form-element-valid-border-color: #{color.mix($jade-450, $zinc-350)};
#{$css-var-prefix}form-element-valid-active-border-color: #{color.mix($jade-450, $zinc-350, 75%)};
#{$css-var-prefix}form-element-valid-focus-color: var(#{$css-var-prefix}form-element-valid-active-border-color);
// Focus for buttons, radio and select
input:is([type='submit'], [type='button'], [type='reset'], [type='checkbox'], [type='radio'], [type='file']) {
#{$css-var-prefix}form-element-focus-color: var(#{$css-var-prefix}primary-focus);
}
}
// Range (input[type="range"])
@if map.get($modules, 'forms/input-range') {
#{$css-var-prefix}range-border-color: #{$slate-100};
#{$css-var-prefix}range-active-border-color: #{$slate-200};
#{$css-var-prefix}range-thumb-border-color: var(#{$css-var-prefix}background-color);
#{$css-var-prefix}range-thumb-color: var(#{$css-var-prefix}primary-background);
#{$css-var-prefix}range-thumb-active-color: var(#{$css-var-prefix}primary-hover-background);
}
// Form validation icons
@if map.get($modules, 'forms/basics') {
#{$css-var-prefix}icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{functions.display-rgb(color.mix($jade-450, $zinc-350))}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
#{$css-var-prefix}icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{functions.display-rgb(color.mix($red-500, $zinc-350, 75%))}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
}
// Document
color-scheme: light;
}

View file

@ -0,0 +1,37 @@
@use 'sass:map';
@use '@picocss/pico/scss/settings' as *;
@use './light';
@use './dark';
/**
* Color schemes
*/
// Light color scheme (Default)
// Can be forced with data-theme="light"
[data-theme='light'],
:root:not([data-theme='dark']) {
@include light.theme;
}
// Dark color scheme (Auto)
// Automatically enabled if user has Dark mode enabled
@media only screen and (prefers-color-scheme: dark) {
:root:not([data-theme]) {
@include dark.theme;
}
}
// Dark color scheme (Forced)
// Enabled if forced with data-theme="dark"
[data-theme='dark'] {
@include dark.theme;
}
progress,
[type='checkbox'],
[type='radio'],
[type='range'] {
accent-color: var(#{$css-var-prefix}primary);
}

View file

@ -0,0 +1,187 @@
@use 'sass:map';
@use '@picocss/pico/scss/settings' as *;
@use '@picocss/pico/scss/colors' as *;
@use '@picocss/pico/scss/helpers/functions';
/**
* Styles
*/
:root {
// Typography
#{$css-var-prefix}font-family-emoji: 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
#{$css-var-prefix}font-family-sans-serif: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
var(#{$css-var-prefix}font-family-emoji);
#{$css-var-prefix}font-family-monospace: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono',
monospace, var(#{$css-var-prefix}font-family-emoji);
#{$css-var-prefix}font-family: var(#{$css-var-prefix}font-family-sans-serif);
#{$css-var-prefix}line-height: 1.5;
#{$css-var-prefix}font-weight: 400;
#{$css-var-prefix}font-size: 100%;
#{$css-var-prefix}text-underline-offset: 0.1rem;
// Borders
#{$css-var-prefix}border-radius: 0.375rem;
#{$css-var-prefix}border-width: 0.0625rem;
#{$css-var-prefix}outline-width: 0.125rem;
// Transitions
#{$css-var-prefix}transition: 0.2s ease-in-out;
// Spacings
#{$css-var-prefix}spacing: 1rem;
// Spacings for typography elements
@if map.get($modules, 'content/typography') {
& {
#{$css-var-prefix}typography-spacing-vertical: 1rem;
}
}
// Spacings for body > header, body > main, body > footer, section, article
@if map.get($modules, 'layout/landmarks') or
map.get($modules, 'layout/section') or
map.get($modules, 'components/card') or
map.get($modules, 'components/modal')
{
& {
#{$css-var-prefix}block-spacing-vertical: calc(var(#{$css-var-prefix}spacing) * 1.5);
#{$css-var-prefix}block-spacing-horizontal: calc(var(#{$css-var-prefix}spacing) * 1.5);
}
}
// Spacings for form elements and button
@if map.get($modules, 'content/button') or map.get($modules, 'forms/basic') {
& {
#{$css-var-prefix}form-element-spacing-vertical: 0.75rem;
#{$css-var-prefix}form-element-spacing-horizontal: 1rem;
}
}
// Font weight for form labels & fieldsets legend
@if map.get($modules, 'forms/basic') {
& {
#{$css-var-prefix}form-label-font-weight: var(#{$css-var-prefix}font-weight);
}
}
// Checkboxes icons
@if map.get($modules, 'forms/checkbox-radio-switch') {
& {
#{$css-var-prefix}icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{functions.display-rgb($white)}' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
#{$css-var-prefix}icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{functions.display-rgb($white)}' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");
}
}
// Chevron icons
@if map.get($modules, 'forms/basics') or
map.get($modules, 'components/accordion') or
map.get($modules, 'components/dropdown')
{
& {
#{$css-var-prefix}icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='#{functions.display-rgb($zinc-400)}' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
}
}
// Responsive root font size
@if $enable-responsive-typography {
@each $key, $values in $breakpoints {
@if $values {
@media (min-width: map.get($values, 'breakpoint')) {
#{$css-var-prefix}font-size: map.get($values, 'root-font-size');
}
}
}
}
}
// Link
@if map.get($modules, 'content/link') {
a {
#{$css-var-prefix}text-decoration: underline;
// Secondary & Contrast
@if enable-classes {
&.secondary,
&.contrast {
#{$css-var-prefix}text-decoration: underline;
}
}
}
}
// Typography
@if map.get($modules, 'content/typography') {
// Small
small {
#{$css-var-prefix}font-size: 0.875em;
}
// Headings
h1,
h2,
h3,
h4,
h5,
h6 {
#{$css-var-prefix}font-weight: 700;
}
h1 {
#{$css-var-prefix}font-size: 2rem;
#{$css-var-prefix}line-height: 1.125;
#{$css-var-prefix}typography-spacing-top: 3rem;
}
h2 {
#{$css-var-prefix}font-size: 1.75rem;
#{$css-var-prefix}line-height: 1.15;
#{$css-var-prefix}typography-spacing-top: 2.625rem;
}
h3 {
#{$css-var-prefix}font-size: 1.5rem;
#{$css-var-prefix}line-height: 1.175;
#{$css-var-prefix}typography-spacing-top: 2.25rem;
}
h4 {
#{$css-var-prefix}font-size: 1.25rem;
#{$css-var-prefix}line-height: 1.2;
#{$css-var-prefix}typography-spacing-top: 1.874rem;
}
h5 {
#{$css-var-prefix}font-size: 1.125rem;
#{$css-var-prefix}line-height: 1.225;
#{$css-var-prefix}typography-spacing-top: 1.6875rem;
}
h6 {
#{$css-var-prefix}font-size: 1rem;
#{$css-var-prefix}line-height: 1.25;
#{$css-var-prefix}typography-spacing-top: 1.5rem;
}
}
// Buttons
button,
[type='submit'],
[type='reset'],
[type='button'],
[type='file']::file-selector-button,
[role='button'] {
#{$css-var-prefix}font-weight: 700;
}
// Table
@if map.get($modules, 'content/table') {
thead,
tfoot {
th,
td {
#{$css-var-prefix}font-weight: 600;
#{$css-var-prefix}border-width: 0.1875rem;
}
}
}

View file

@ -1,18 +0,0 @@
h1,
h2,
h3 {
font-family: Avenir, Montserrat, Corbel, 'URW Gothic', source-sans-pro, sans-serif;
font-weight: 500;
}
h1 {
font-size: 5.7rem;
}
h2 {
font-size: 4.5rem;
}
h3 {
font-size: 3.6rem;
}