Fiddling around more with login
This commit is contained in:
parent
d0c9d851f6
commit
16bac8e9cf
20 changed files with 602 additions and 224 deletions
7
webapp/package-lock.json
generated
7
webapp/package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-card',
|
||||
templateUrl: './card.html',
|
||||
styleUrl: './card.scss',
|
||||
})
|
||||
export class Card {}
|
|
@ -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">
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
:host {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
form {
|
||||
fieldset,
|
||||
input[type='submit'] {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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!
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-theme="light">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Webapp</title>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
144
webapp/src/styles/theme/_dark.scss
Normal file
144
webapp/src/styles/theme/_dark.scss
Normal 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;
|
||||
}
|
144
webapp/src/styles/theme/_light.scss
Normal file
144
webapp/src/styles/theme/_light.scss
Normal 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;
|
||||
}
|
37
webapp/src/styles/theme/_schemes.scss
Normal file
37
webapp/src/styles/theme/_schemes.scss
Normal 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);
|
||||
}
|
187
webapp/src/styles/theme/_styles.scss
Normal file
187
webapp/src/styles/theme/_styles.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Add table
Reference in a new issue