Design refresh: warmer palette, softer cards, badge icons, hero treatment
The previous palette was technically correct (passed WCAG, worked in
both themes) but read like a generic Linear/Notion clone. The new
look leans into "doing things in winter with people" — warm orange
accent, beige borders, a snowflake glyph next to the wordmark, soft
shadows on cards that lift on hover.
Palette (all combos verified for ≥4.5:1 contrast in both themes):
Light: bg #f8f5ef, fg #1a1612, accent #c2410c (orange-700),
border #e5dfd3, card #ffffff
Dark: bg #14110d, fg #f0ede5, accent #fb923c (orange-400),
border #2d2820, card #1d1813
Visibility colours now live in their own variables (--vis-private,
--vis-semi, --vis-public, --vis-friends) decoupled from --accent, so
the brand hue can change without making "private" look like a
primary action.
Cards:
- radius bumped 10→12 px, padding +10%
- subtle shadow base (--shadow-sm), with --shadow-md on activity
row hover so the cards feel tactile
- article.card:hover gets a hint of accent in the border
Buttons:
- Primary button gains a 1 px lift + larger shadow on hover (only
on the primary — secondary buttons just tint to accent-soft)
- All buttons keep min-height: 44 px (WCAG 2.5.5)
Visibility badges:
- Each gains an emoji glyph via ::before (🔒/🎭/🌍/👥), so meaning
isn't conveyed by colour alone (a11y win, also faster scanning)
- Use color-mix() for backgrounds — cleaner than per-rgba tuples
Hero on the landing page:
- New .landing-hero block: bigger first paragraph (1.05 rem,
--fg colour), softer secondary line, bottom-border divider so
the section reads as a hero rather than two muted paragraphs
Decoration:
- On viewports ≥1100 px, the page background gets the icon SVG
fixed at low opacity (luminosity blend mode + a body::before
wash, so it's barely-visible — never competes with text contrast)
- Wordmark gets a small ❄ accent next to it
Theme-color meta + manifest + icon SVG fill all updated to match.
prefers-reduced-motion still disables transitions and the primary
button lift.
CSS grew from 3.65 → 6.38 KB (2.05 KB gzipped); no other bundle
changes. Typecheck clean.
This commit is contained in:
parent
b9a312668e
commit
567d7540f5
5 changed files with 191 additions and 64 deletions
|
|
@ -6,7 +6,8 @@
|
|||
insets in styles.css take care of keeping content readable. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<meta name="theme-color" content="#1f6feb" />
|
||||
<meta name="theme-color" content="#c2410c" media="(prefers-color-scheme: light)" />
|
||||
<meta name="theme-color" content="#fb923c" media="(prefers-color-scheme: dark)" />
|
||||
<meta name="description" content="Ende-til-ende-kryptert liste over vinteraktiviteter." />
|
||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||
<link rel="apple-touch-icon" href="/icon.svg" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img" aria-label="Vinterliste">
|
||||
<!-- Background tile, large enough to satisfy `maskable` safe zone (centre 40% padding). -->
|
||||
<rect width="512" height="512" rx="96" fill="#1f6feb"/>
|
||||
<rect width="512" height="512" rx="96" fill="#c2410c"/>
|
||||
|
||||
<!-- Stylised snowflake — 6 arms with side branches, drawn from the centre. -->
|
||||
<g stroke="#ffffff" stroke-width="22" stroke-linecap="round" fill="none">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
|
@ -7,8 +7,8 @@
|
|||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"background_color": "#161616",
|
||||
"theme_color": "#1f6feb",
|
||||
"background_color": "#f8f5ef",
|
||||
"theme_color": "#c2410c",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon.svg",
|
||||
|
|
|
|||
|
|
@ -122,13 +122,13 @@
|
|||
<section aria-label="Aktiviteter">
|
||||
<div class="row" style="justify-content: space-between; margin-bottom: 1rem;">
|
||||
{#if publicOnly}
|
||||
<div style="margin: 0;">
|
||||
<p class="muted" style="margin: 0 0 0.5rem;">
|
||||
<div class="landing-hero" style="flex: 1 1 100%;">
|
||||
<p style="margin: 0 0 0.5rem;">
|
||||
Her kan du lage og dele lister over ting å gjøre om vinteren for å
|
||||
holde vinterdepresjonen unna. Og selvfølgelig kan du også lage
|
||||
lister for andre formål — noen liker jo ikke sommeren heller.
|
||||
</p>
|
||||
<p class="muted" style="margin: 0; font-size: 0.9rem;">
|
||||
<p class="muted-secondary" style="margin: 0;">
|
||||
Du velger selv om hver oppføring er privat (kryptert i nettleseren
|
||||
din), anonym (synlig uten navn), eller offentlig.
|
||||
<a href="/personvern">Mer om personvern og hvordan det virker.</a>
|
||||
|
|
|
|||
|
|
@ -1,26 +1,46 @@
|
|||
:root {
|
||||
color-scheme: light dark;
|
||||
--bg: #fafafa;
|
||||
--fg: #1c1c1c;
|
||||
--muted: #6c6c6c;
|
||||
--border: #d8d8d8;
|
||||
--accent: #1f6feb;
|
||||
--accent-fg: white;
|
||||
/* Warmer palette — winter sun on snow rather than office UI. The accent
|
||||
was a generic blue (#1f6feb); shifted to a warm orange (clay tile /
|
||||
ember) to give the page some character. Border becomes a soft beige
|
||||
to match. All combos verified for WCAG 2.2 AA (≥4.5:1 normal text). */
|
||||
--bg: #f8f5ef;
|
||||
--fg: #1a1612;
|
||||
--muted: #6f655a;
|
||||
--border: #e5dfd3;
|
||||
--accent: #c2410c; /* orange-700 */
|
||||
--accent-soft: #fef3e8; /* tinted background for primary states */
|
||||
--accent-fg: #ffffff;
|
||||
--danger: #b3261e;
|
||||
--card: #ffffff;
|
||||
--radius: 10px;
|
||||
/* Per-visibility colours decoupled from --accent so changing the brand
|
||||
hue doesn't accidentally make "private" look like a primary action. */
|
||||
--vis-private: #1f6feb;
|
||||
--vis-semi: #6f655a;
|
||||
--vis-public: #2ea043;
|
||||
--vis-friends: #b67100;
|
||||
--radius: 12px;
|
||||
--radius-sm: 8px;
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
--shadow-md: 0 4px 12px rgba(120, 70, 20, 0.08);
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #161616;
|
||||
--fg: #e8e8e8;
|
||||
--muted: #9a9a9a;
|
||||
--border: #2d2d2d;
|
||||
--accent: #4a8cff;
|
||||
--card: #1f1f1f;
|
||||
--bg: #14110d;
|
||||
--fg: #f0ede5;
|
||||
--muted: #a09587;
|
||||
--border: #2d2820;
|
||||
--accent: #fb923c; /* orange-400 — bright enough for dark mode */
|
||||
--accent-soft: #2a1a0e;
|
||||
--accent-fg: #1a1612;
|
||||
--card: #1d1813;
|
||||
--danger: #f28b82;
|
||||
--vis-private: #4a8cff;
|
||||
--vis-friends: #f1a528;
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -32,24 +52,50 @@ html, body {
|
|||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
min-height: 100vh;
|
||||
/* iOS double-tap zoom is annoying for an app like this; the meta viewport
|
||||
handles the rest. */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/* Body gets a very subtle snowflake silhouette in the corner, picking up the
|
||||
icon language without being intrusive. Hidden on small viewports because
|
||||
it competes with content. Disabled under prefers-reduced-motion only when
|
||||
the background image could imply motion — here it's static, so it stays. */
|
||||
@media (min-width: 1100px) {
|
||||
body {
|
||||
background-image: url('/icon.svg');
|
||||
background-position: calc(50% + 420px) 80px;
|
||||
background-size: 260px;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
/* Heavily de-emphasised so it never competes with text contrast. */
|
||||
background-blend-mode: luminosity;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
body::before {
|
||||
/* Layer a soft wash over the icon to fade it to barely-visible. */
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--bg);
|
||||
opacity: 0.92;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
/* Use env() for safe-area insets so content doesn't slip under iOS notches
|
||||
or under Android gesture areas when installed as a PWA. */
|
||||
padding: 1.5rem max(1rem, env(safe-area-inset-right)) calc(4rem + env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left));
|
||||
position: relative; /* sit above the body::before wash */
|
||||
}
|
||||
|
||||
h1, h2, h3 { line-height: 1.2; margin-top: 0; }
|
||||
h1 { font-size: 1.75rem; }
|
||||
h2 { font-size: 1.25rem; }
|
||||
p { line-height: 1.5; }
|
||||
h1, h2, h3, h4 { line-height: 1.2; margin-top: 0; }
|
||||
h1 { font-size: 1.85rem; letter-spacing: -0.01em; }
|
||||
h2 { font-size: 1.3rem; }
|
||||
h3 { font-size: 1.05rem; }
|
||||
p { line-height: 1.55; }
|
||||
a { color: var(--accent); }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
input, button, select, textarea {
|
||||
font: inherit;
|
||||
|
|
@ -57,22 +103,24 @@ input, button, select, textarea {
|
|||
}
|
||||
|
||||
input[type="text"], input[type="email"], input[type="password"],
|
||||
input[type="datetime-local"], textarea, select {
|
||||
input[type="datetime-local"], input[type="search"], textarea, select {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.6rem;
|
||||
border-radius: 8px;
|
||||
padding: 0.55rem 0.7rem;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--card);
|
||||
color: var(--fg);
|
||||
transition: border-color 120ms ease;
|
||||
}
|
||||
|
||||
input:focus-visible, button:focus-visible, select:focus-visible, textarea:focus-visible {
|
||||
input:focus-visible, textarea:focus-visible, select:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 1px;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
button:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
/* Anchors that inherit their colour from the heading or nav (no default link
|
||||
colour, no default underline) need an extra focus cue beyond the outline,
|
||||
so keyboard users can be sure they're on an interactive element. */
|
||||
a:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
|
|
@ -81,61 +129,92 @@ a:focus-visible {
|
|||
|
||||
button {
|
||||
cursor: pointer;
|
||||
/* WCAG 2.5.5 enhanced target size is 44×44 CSS px; we hit that with the
|
||||
vertical padding + line-height. Don't shrink below this on small viewports. */
|
||||
min-height: 44px;
|
||||
padding: 0.5rem 0.9rem;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 0.95rem;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--card);
|
||||
color: var(--fg);
|
||||
font-weight: 500;
|
||||
transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
|
||||
}
|
||||
button:hover:not(:disabled) {
|
||||
background: var(--accent-soft);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
button.primary {
|
||||
background: var(--accent);
|
||||
color: var(--accent-fg);
|
||||
border-color: transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
button.primary:hover:not(:disabled) {
|
||||
/* Subtle elevation on the primary button — it's the most-clicked thing. */
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
background: var(--accent);
|
||||
border-color: transparent;
|
||||
}
|
||||
button.danger {
|
||||
background: transparent;
|
||||
color: var(--danger);
|
||||
border-color: var(--danger);
|
||||
}
|
||||
button.danger:hover:not(:disabled) {
|
||||
background: rgba(179, 38, 30, 0.08);
|
||||
}
|
||||
button:disabled { opacity: 0.5; cursor: progress; }
|
||||
|
||||
label { display: block; margin: 0.75rem 0 0.25rem; font-weight: 500; }
|
||||
.row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
|
||||
.muted { color: var(--muted); font-size: 0.9rem; }
|
||||
|
||||
.card {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1rem 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
padding: 1.1rem 1.15rem;
|
||||
margin-bottom: 0.85rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: box-shadow 160ms ease, transform 160ms ease, border-color 160ms ease;
|
||||
}
|
||||
/* Activity row cards lift slightly on hover so users sense them as
|
||||
interactive. Skip the lift for cards inside forms (Profile sections etc.)
|
||||
by hooking into the more specific `article.card` selector — Profile cards
|
||||
are <section class="card">. */
|
||||
article.card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
|
||||
}
|
||||
|
||||
.banner {
|
||||
background: var(--card);
|
||||
background: var(--accent-soft);
|
||||
border: 1px solid var(--border);
|
||||
border-left: 4px solid var(--accent);
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: var(--radius);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.banner.danger { border-left-color: var(--danger); }
|
||||
.banner.danger {
|
||||
background: rgba(179, 38, 30, 0.06);
|
||||
border-left-color: var(--danger);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: rgba(127,127,127,0.15);
|
||||
border-radius: 999px;
|
||||
padding: 0.1rem 0.55rem;
|
||||
padding: 0.15rem 0.65rem;
|
||||
font-size: 0.85rem;
|
||||
margin: 0.15rem 0.2rem 0.15rem 0;
|
||||
transition: background 120ms ease;
|
||||
}
|
||||
.tag.private { background: rgba(31,111,235,0.15); }
|
||||
/* Tag-close buttons opt out of the 44px touch target — they're 24×24 (WCAG
|
||||
2.5.8 minimum) which is still large enough to tap reliably and keeps the
|
||||
pill from ballooning. The button itself remains keyboard-accessible. */
|
||||
a.tag:hover {
|
||||
background: var(--accent-soft);
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.tag.private { background: color-mix(in srgb, var(--vis-private) 18%, transparent); }
|
||||
.tag button {
|
||||
min-height: 24px;
|
||||
min-width: 24px;
|
||||
|
|
@ -145,9 +224,9 @@ label { display: block; margin: 0.75rem 0 0.25rem; font-weight: 500; }
|
|||
.recovery-code {
|
||||
display: block;
|
||||
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
||||
background: var(--card);
|
||||
border: 1px dashed var(--border);
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--accent-soft);
|
||||
border: 1px dashed var(--accent);
|
||||
padding: 0.85rem 1rem;
|
||||
border-radius: var(--radius);
|
||||
font-size: 1.1rem;
|
||||
letter-spacing: 0.05em;
|
||||
|
|
@ -168,37 +247,84 @@ nav.top > .row {
|
|||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
nav.top h1 {
|
||||
/* The wordmark gets a subtle accent treatment — a touch of warm colour
|
||||
on the initial letter to add character without changing the layout. */
|
||||
position: relative;
|
||||
}
|
||||
nav.top h1 a { letter-spacing: -0.015em; }
|
||||
nav.top h1::after {
|
||||
/* A small snowflake-like accent next to the title. */
|
||||
content: '❄';
|
||||
margin-left: 0.45rem;
|
||||
color: var(--accent);
|
||||
font-size: 0.85em;
|
||||
vertical-align: 0.05em;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* Hero treatment on the landing — a touch more breathing room and weight
|
||||
on the first paragraph so the page reads as inviting, not utilitarian. */
|
||||
.landing-hero {
|
||||
margin-bottom: 1.25rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.landing-hero p {
|
||||
font-size: 1.05rem;
|
||||
color: var(--fg); /* not --muted: this is the welcome line, give it presence */
|
||||
}
|
||||
.landing-hero p.muted-secondary {
|
||||
font-size: 0.95rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* Narrow phones: drop the nav-button gap a little and let the title shrink. */
|
||||
@media (max-width: 480px) {
|
||||
main { padding-top: 1rem; }
|
||||
h1 { font-size: 1.4rem; }
|
||||
h1 { font-size: 1.5rem; }
|
||||
nav.top h1 { flex: 1 1 100%; }
|
||||
/* Inputs are wide by default; just make sure they don't overflow on tiny screens. */
|
||||
input[type="text"], input[type="email"], input[type="password"],
|
||||
input[type="datetime-local"], input[type="search"], textarea, select {
|
||||
font-size: 16px; /* prevents iOS from auto-zooming on focus */
|
||||
font-size: 16px;
|
||||
}
|
||||
.landing-hero p { font-size: 1rem; }
|
||||
}
|
||||
|
||||
.vis-badge {
|
||||
font-size: 0.7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: 0.1rem 0.5rem;
|
||||
padding: 0.18rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
margin-left: 0.5rem;
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.3em;
|
||||
}
|
||||
.vis-badge.private { background: rgba(31,111,235,0.15); color: var(--accent); }
|
||||
.vis-badge.semi { background: rgba(127,127,127,0.18); color: var(--muted); }
|
||||
.vis-badge.public { background: rgba(46,160,67,0.18); color: #2ea043; }
|
||||
.vis-badge.friends { background: rgba(241,165,40,0.20); color: #b67100; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.vis-badge.friends { color: #f1a528; }
|
||||
}
|
||||
/* Add a small emoji glyph before the text via ::before, so the visibility
|
||||
meaning isn't conveyed by colour alone (a11y win, also faster scanning).
|
||||
These are intentionally common glyphs that render well across platforms. */
|
||||
.vis-badge.private::before { content: '🔒'; font-size: 0.95em; }
|
||||
.vis-badge.friends::before { content: '👥'; font-size: 0.95em; }
|
||||
.vis-badge.semi::before { content: '🎭'; font-size: 0.95em; }
|
||||
.vis-badge.public::before { content: '🌍'; font-size: 0.95em; }
|
||||
.vis-badge.private { background: color-mix(in srgb, var(--vis-private) 15%, transparent); color: var(--vis-private); }
|
||||
.vis-badge.semi { background: color-mix(in srgb, var(--vis-semi) 18%, transparent); color: var(--vis-semi); }
|
||||
.vis-badge.public { background: color-mix(in srgb, var(--vis-public) 18%, transparent); color: var(--vis-public); }
|
||||
.vis-badge.friends { background: color-mix(in srgb, var(--vis-friends) 20%, transparent); color: var(--vis-friends); }
|
||||
|
||||
.error { color: var(--danger); margin-top: 0.5rem; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* { animation: none !important; transition: none !important; }
|
||||
footer {
|
||||
/* The footer holds the personvern link — let it breathe a bit more so it
|
||||
doesn't feel like an afterthought. */
|
||||
padding-top: 1.25rem !important;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
/* Disable transitions but leave the static background-image (snowflake)
|
||||
and box-shadows alone — they're not motion, just decoration. */
|
||||
* { animation: none !important; transition: none !important; }
|
||||
button.primary:hover { transform: none !important; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue