0.2, updated UI
Build and Publish Docker Image / Build and Push Docker Image (push) Successful in 2m44s
Details
Build and Publish Docker Image / Build and Push Docker Image (push) Successful in 2m44s
Details
This commit is contained in:
parent
c2b80047ae
commit
f8acc55f67
|
|
@ -0,0 +1,5 @@
|
||||||
|
shots/
|
||||||
|
before/
|
||||||
|
before.json
|
||||||
|
before.txt
|
||||||
|
compare.html
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"mode": "redesign",
|
||||||
|
"depth": "full",
|
||||||
|
"scope": "page",
|
||||||
|
"shape": "Workbench",
|
||||||
|
"theme": "studio",
|
||||||
|
"axes": "light/roman-serif/warm",
|
||||||
|
"hero": "Stage",
|
||||||
|
"nav": "edge-aligned",
|
||||||
|
"footer": "letter close",
|
||||||
|
"imagery": "the product",
|
||||||
|
"photos": [],
|
||||||
|
"entry": "app.py",
|
||||||
|
"fingerprint": "asymmetric-grid/serif-display/proof-sheet/hairline-set/colophon-close/restrained",
|
||||||
|
"sections": [
|
||||||
|
"Hero (Stage)",
|
||||||
|
"Bench notes (Set)",
|
||||||
|
"Colophon (Footer)"
|
||||||
|
],
|
||||||
|
"signature": "crop-marked proof sheet with orange metric construction guides",
|
||||||
|
"waived": [],
|
||||||
|
"prompt": "Use the hallmark skill to give this web app a professional clean look for a more hip modern dad looking for a joke",
|
||||||
|
"date": "2026-10-30"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"scanned": "2026-10-29",
|
||||||
|
"findings": {
|
||||||
|
"fonts": "System sans (-apple-system, BlinkMacSystemFont, Segoe UI, Roboto) + Georgia serif quote mark (app.py L43, L174)",
|
||||||
|
"palette": "Hardcoded hex literals (#ff0844, #ff4b2b, #ffdd00, #00d2ff, #0052d4, Slate #0f172a..#f8fafc) in inline <style> (app.py L45-L270)",
|
||||||
|
"motion": "CSS keyframes (gradientShift, floatOrbs, spin) + transition: all (app.py L47, L62, L213, L241)",
|
||||||
|
"spacing": "Ad-hoc pixel values (24px, 44px, 38px, 26px, 14px, 28px) (app.py L42-L275)",
|
||||||
|
"framework": "Flask 3.0.0 with inline render_template_string (app.py L4, L24)"
|
||||||
|
},
|
||||||
|
"preserve": [
|
||||||
|
"Flask routes (/ and /api/joke)",
|
||||||
|
"Ollama integration and version.ini metadata",
|
||||||
|
"DOM hooks (#refreshBtn, #btnText, #spinner, #joke, fetchNewJoke())"
|
||||||
|
],
|
||||||
|
"introduce": [
|
||||||
|
"Hallmark token system",
|
||||||
|
"Editorial theme & typography",
|
||||||
|
"Accessible focus, reduced-motion, and control states"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
3aca28a0
|
||||||
812
app.py
812
app.py
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import configparser
|
import configparser
|
||||||
import requests
|
import requests
|
||||||
from flask import Flask, render_template_string, jsonify
|
from flask import Flask, render_template_string, jsonify, send_from_directory
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
@ -26,320 +26,652 @@ HTML_TEMPLATE = """<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Programming Dad Jokes</title>
|
<meta name="description" content="Dry programming dad jokes set on a typographic studio proof sheet, pulled live from your local Ollama model." />
|
||||||
|
<title>Programming Dad Joke</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Hedvig+Letters+Serif:opsz@12..24&family=Geist:wght@400;500&display=swap" rel="stylesheet" />
|
||||||
<style>
|
<style>
|
||||||
* {
|
/* Hallmark · shape: Workbench (draw 9cbbc9) · genre: editorial · theme: Studio (deal 3/3, seed programming-dad-joke-generator-254f·3aca28a0·2026-10-30·r0) · redesign: full
|
||||||
box-sizing: border-box;
|
* hero: Stage · nav: edge-aligned · footer: letter close · imagery: the product · signature: crop-marked proof sheet with orange metric construction guides
|
||||||
margin: 0;
|
* axes: light/roman-serif/warm · posture: restrained · contrast: pass · fold: pass · v2.0.0 */
|
||||||
padding: 0;
|
/* Hallmark · pre-emit critique: P5 H5 E5 S4 R5 V5 · weakest: S, generic joke card could fit any trivia app; framed the joke as a crop-marked Studio proof sheet with live typographic metric guides */
|
||||||
}
|
/* Hallmark · reworded "Tell Me Another Joke! 🚀" to "Pull another joke" · 30a bans emoji in controls */
|
||||||
|
/* Hallmark · reworded "Freshly compiled humor from our AI model" to "Dry humor for engineers after hours, pulled live from your local model." · 19 names generic AI filler */
|
||||||
|
/* Hallmark · dropped "Ollama • Gemma 4" · 54 and 63 ban middle-dot eyebrow badges */
|
||||||
|
@import url("./tokens.css");
|
||||||
|
@import url("./base.css");
|
||||||
|
|
||||||
body {
|
.skip {
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 24px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
||||||
/* Vibrant animated gradient with bright red, yellow, and blue */
|
|
||||||
background: linear-gradient(-45deg, #ff0844 0%, #ff4b2b 20%, #ffdd00 45%, #00d2ff 75%, #0052d4 100%);
|
|
||||||
background-size: 400% 400%;
|
|
||||||
animation: gradientShift 14s ease infinite;
|
|
||||||
position: relative;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ambient glowing gradient orbs for depth */
|
|
||||||
body::before,
|
|
||||||
body::after {
|
|
||||||
content: "";
|
|
||||||
position: fixed;
|
|
||||||
border-radius: 50%;
|
|
||||||
filter: blur(80px);
|
|
||||||
z-index: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.65;
|
|
||||||
animation: floatOrbs 10s ease-in-out infinite alternate;
|
|
||||||
}
|
|
||||||
|
|
||||||
body::before {
|
|
||||||
width: 380px;
|
|
||||||
height: 380px;
|
|
||||||
background: radial-gradient(circle, #ff0055 0%, #ffaa00 100%);
|
|
||||||
top: -80px;
|
|
||||||
left: -80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body::after {
|
|
||||||
width: 420px;
|
|
||||||
height: 420px;
|
|
||||||
background: radial-gradient(circle, #00d2ff 0%, #0044ff 100%);
|
|
||||||
bottom: -100px;
|
|
||||||
right: -100px;
|
|
||||||
animation-delay: -5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes gradientShift {
|
|
||||||
0% { background-position: 0% 50%; }
|
|
||||||
50% { background-position: 100% 50%; }
|
|
||||||
100% { background-position: 0% 50%; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes floatOrbs {
|
|
||||||
0% { transform: translate(0, 0) scale(1); }
|
|
||||||
100% { transform: translate(40px, 40px) scale(1.1); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 680px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background: rgba(255, 255, 255, 0.92);
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
border-radius: 28px;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.85);
|
|
||||||
box-shadow: 0 25px 60px rgba(0, 0, 0, 0.22),
|
|
||||||
0 0 35px rgba(255, 255, 255, 0.35);
|
|
||||||
padding: 44px 38px;
|
|
||||||
text-align: center;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 6px 16px;
|
|
||||||
border-radius: 999px;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 0.08em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: #1e293b;
|
|
||||||
background: linear-gradient(90deg, rgba(255, 75, 43, 0.15), rgba(255, 221, 0, 0.25), rgba(0, 210, 255, 0.2));
|
|
||||||
border: 1px solid rgba(255, 75, 43, 0.3);
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-dot {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #10b981;
|
|
||||||
box-shadow: 0 0 8px #10b981;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-divider {
|
|
||||||
opacity: 0.4;
|
|
||||||
margin: 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2.2rem;
|
|
||||||
font-weight: 800;
|
|
||||||
color: #0f172a;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 1rem;
|
|
||||||
color: #64748b;
|
|
||||||
margin-bottom: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.joke-box {
|
|
||||||
background: #f8fafc;
|
|
||||||
border-radius: 18px;
|
|
||||||
padding: 26px 24px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
position: relative;
|
|
||||||
border: 1px solid #e2e8f0;
|
|
||||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.02);
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.joke-box::before {
|
|
||||||
content: '"';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -12px;
|
left: var(--space-md);
|
||||||
left: 18px;
|
top: calc(-1 * var(--space-3xl));
|
||||||
font-size: 3.5rem;
|
z-index: var(--z-toast);
|
||||||
line-height: 1;
|
padding: var(--space-xs) var(--space-md);
|
||||||
font-family: Georgia, serif;
|
background: var(--color-ink);
|
||||||
color: #ff0844;
|
color: var(--color-paper);
|
||||||
opacity: 0.3;
|
text-decoration: none;
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.joke-text {
|
.skip:focus-visible {
|
||||||
font-size: 1.25rem;
|
top: var(--space-md);
|
||||||
line-height: 1.7;
|
}
|
||||||
color: #1e293b;
|
|
||||||
font-weight: 500;
|
.proof {
|
||||||
white-space: pre-wrap;
|
background-image: radial-gradient(circle at 1px 1px, var(--color-dot) 1px, transparent 1.4px);
|
||||||
word-break: break-word;
|
background-size: 24px 24px;
|
||||||
min-height: 80px;
|
}
|
||||||
|
|
||||||
|
.crop {
|
||||||
|
position: relative;
|
||||||
|
--l: 8px;
|
||||||
|
--o: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crop::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: calc(-1 * var(--o));
|
||||||
|
pointer-events: none;
|
||||||
|
background:
|
||||||
|
linear-gradient(var(--color-mark), var(--color-mark)) 0 var(--o) / var(--l) 1px no-repeat,
|
||||||
|
linear-gradient(var(--color-mark), var(--color-mark)) var(--o) 0 / 1px var(--l) no-repeat,
|
||||||
|
linear-gradient(var(--color-mark), var(--color-mark)) 100% var(--o) / var(--l) 1px no-repeat,
|
||||||
|
linear-gradient(var(--color-mark), var(--color-mark)) calc(100% - var(--o)) 0 / 1px var(--l) no-repeat,
|
||||||
|
linear-gradient(var(--color-mark), var(--color-mark)) 0 calc(100% - var(--o)) / var(--l) 1px no-repeat,
|
||||||
|
linear-gradient(var(--color-mark), var(--color-mark)) var(--o) 100% / 1px var(--l) no-repeat,
|
||||||
|
linear-gradient(var(--color-mark), var(--color-mark)) 100% calc(100% - var(--o)) / var(--l) 1px no-repeat,
|
||||||
|
linear-gradient(var(--color-mark), var(--color-mark)) calc(100% - var(--o)) 100% / 1px var(--l) no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header {
|
||||||
|
border-bottom: var(--rule-hair) solid var(--color-rule);
|
||||||
|
padding-block: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header__inner {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wordmark {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: var(--text-lg);
|
||||||
|
font-weight: var(--display-weight);
|
||||||
|
letter-spacing: var(--tracking-display);
|
||||||
|
color: var(--color-ink);
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wordmark:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-lg);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
color: var(--color-ink-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav a {
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
padding-block: var(--space-3xl);
|
||||||
|
border-bottom: var(--rule-hair) solid var(--color-rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
|
||||||
|
gap: var(--gap-columns);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero__lead {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero__lead .lead {
|
||||||
|
color: var(--color-ink-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
gap: 14px;
|
gap: var(--space-md);
|
||||||
margin-bottom: 24px;
|
margin-top: var(--space-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
|
position: relative;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 700;
|
|
||||||
padding: 14px 28px;
|
|
||||||
border-radius: 14px;
|
|
||||||
color: #ffffff;
|
|
||||||
background: linear-gradient(135deg, #ff0844 0%, #ff8c00 40%, #0099ff 100%);
|
|
||||||
background-size: 200% 200%;
|
|
||||||
box-shadow: 0 10px 25px rgba(255, 8, 68, 0.35);
|
|
||||||
transition: all 0.25s ease;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
justify-content: center;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
min-height: 48px;
|
||||||
|
padding: 0 var(--space-lg);
|
||||||
|
border: var(--rule-hair) solid var(--color-ink);
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
background: var(--color-paper-hi);
|
||||||
|
color: var(--color-ink);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-md);
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color var(--dur-short) var(--ease-out), transform var(--dur-micro) var(--ease-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: var(--space-3xs);
|
||||||
|
border: var(--rule-hair) solid var(--color-mark);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--primary {
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: var(--color-accent-ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover:not(:disabled) {
|
.btn:hover:not(:disabled) {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 14px 30px rgba(0, 153, 255, 0.45);
|
|
||||||
background-position: 100% 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:active:not(:disabled) {
|
.btn:active:not(:disabled) {
|
||||||
transform: translateY(1px);
|
transform: translateY(1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:disabled {
|
.btn:focus-visible {
|
||||||
opacity: 0.65;
|
outline: 2px solid var(--color-focus);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:disabled,
|
||||||
|
.btn[aria-disabled="true"] {
|
||||||
|
opacity: 0.55;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn[aria-busy="true"] {
|
||||||
|
cursor: progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
display: none;
|
display: none;
|
||||||
width: 18px;
|
width: 16px;
|
||||||
height: 18px;
|
height: 16px;
|
||||||
border: 3px solid rgba(255, 255, 255, 0.35);
|
border: 2px solid var(--color-mark);
|
||||||
border-top-color: #ffffff;
|
border-top-color: var(--color-ink);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 0.8s linear infinite;
|
animation: spin var(--dur-long) linear infinite;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta {
|
.sheet {
|
||||||
font-size: 0.82rem;
|
background: var(--color-paper-hi);
|
||||||
color: #94a3b8;
|
border: var(--rule-hair) solid var(--color-rule);
|
||||||
|
border-radius: var(--radius-card);
|
||||||
|
box-shadow: var(--shadow-sheet);
|
||||||
|
padding: var(--space-xl);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
flex-wrap: wrap;
|
gap: var(--space-lg);
|
||||||
gap: 16px;
|
|
||||||
border-top: 1px solid #f1f5f9;
|
|
||||||
padding-top: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta span {
|
.guide-bar {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: var(--space-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta code {
|
.guide-line {
|
||||||
background: #f1f5f9;
|
flex: 1;
|
||||||
padding: 2px 6px;
|
height: var(--rule-hair);
|
||||||
border-radius: 6px;
|
background: var(--color-accent);
|
||||||
font-size: 0.78rem;
|
|
||||||
color: #475569;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
.guide-readout {
|
||||||
.card {
|
font-size: var(--text-xs);
|
||||||
padding: 30px 20px;
|
font-weight: 500;
|
||||||
|
color: var(--color-muted);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.joke-specimen {
|
||||||
|
margin: 0;
|
||||||
|
padding-block: var(--space-md);
|
||||||
|
border-top: var(--rule-hair) solid var(--color-accent);
|
||||||
|
border-bottom: var(--rule-hair) solid var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.joke-text {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: var(--text-xl);
|
||||||
|
font-weight: var(--display-weight);
|
||||||
|
line-height: 1.4;
|
||||||
|
letter-spacing: var(--tracking-display);
|
||||||
|
color: var(--color-ink);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
min-height: var(--space-3xl);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: opacity var(--dur-short) var(--ease-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet[data-loading="true"] .joke-text {
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proof-progress {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proof-progress__meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proof-progress__track {
|
||||||
|
position: relative;
|
||||||
|
height: var(--space-2xs);
|
||||||
|
background: var(--color-paper-3);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proof-progress__fill {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--color-accent);
|
||||||
|
transform-origin: left center;
|
||||||
|
transform: scaleX(var(--progress, 0));
|
||||||
|
transition: transform var(--dur-short) var(--ease-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section--notes {
|
||||||
|
padding-block: var(--section-gap-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section__head {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
|
||||||
|
gap: var(--gap-columns);
|
||||||
|
align-items: end;
|
||||||
|
margin-bottom: var(--space-2xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section__head p {
|
||||||
|
color: var(--color-ink-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.set {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-top: var(--rule-hair) solid var(--color-ink);
|
||||||
|
counter-reset: rule-counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set__row {
|
||||||
|
counter-increment: rule-counter;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
|
||||||
|
gap: var(--gap-columns);
|
||||||
|
align-items: baseline;
|
||||||
|
padding-block: var(--space-lg);
|
||||||
|
border-bottom: var(--rule-hair) solid var(--color-rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
.set__row h3::before {
|
||||||
|
content: "0" counter(rule-counter) " ";
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-right: var(--space-sm);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set__row p {
|
||||||
|
color: var(--color-ink-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer {
|
||||||
|
background: var(--color-paper-2);
|
||||||
|
color: var(--color-ink);
|
||||||
|
border-top: var(--rule-hair) solid var(--color-ink);
|
||||||
|
padding-block: var(--space-2xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer__head {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-lg);
|
||||||
|
margin-bottom: var(--space-xl);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer__head p {
|
||||||
|
color: var(--color-ink-2);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.colophon-grid {
|
||||||
|
margin: 0;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: var(--gap-columns);
|
||||||
|
border-top: var(--rule-hair) solid var(--color-rule);
|
||||||
|
padding-top: var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.colophon-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2xs);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colophon-item dt {
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-ink-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.colophon-item dd {
|
||||||
|
margin: 0;
|
||||||
|
font-size: var(--text-md);
|
||||||
|
color: var(--color-ink);
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 60rem) {
|
||||||
|
.hero__grid,
|
||||||
|
.section__head,
|
||||||
|
.set__row {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
h1 {
|
|
||||||
font-size: 1.75rem;
|
.colophon-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
.joke-text {
|
}
|
||||||
font-size: 1.1rem;
|
|
||||||
|
@media (max-width: 40rem) {
|
||||||
|
.colophon-grid {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet {
|
||||||
|
padding: var(--space-lg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.btn {
|
||||||
|
transition: background-color var(--dur-short) linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.joke-text,
|
||||||
|
.proof-progress__fill {
|
||||||
|
transition: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<a class="skip" href="#main">Skip to content</a>
|
||||||
<main class="card">
|
|
||||||
<div class="badge">
|
|
||||||
<span class="badge-dot"></span>
|
|
||||||
<span>Ollama • Gemma 4</span>
|
|
||||||
<span class="badge-divider">•</span>
|
|
||||||
<span>v{{ version }}</span>
|
|
||||||
</div>
|
|
||||||
<h1>Programming Dad Joke</h1>
|
|
||||||
<p class="subtitle">Freshly compiled humor from our AI model</p>
|
|
||||||
|
|
||||||
<div class="joke-box">
|
<header class="site-header proof">
|
||||||
<div id="joke" class="joke-text">{{ joke }}</div>
|
<div class="container site-header__inner">
|
||||||
</div>
|
<a href="#main" class="wordmark" aria-label="Programming Dad Joke home">Proof Sheet</a>
|
||||||
|
<nav class="site-nav" aria-label="Page sections">
|
||||||
|
<a href="#specimen">Specimen</a>
|
||||||
|
<a href="#notes">Bench notes</a>
|
||||||
|
<a href="#colophon">Colophon</a>
|
||||||
|
<span class="num">v{{ version }}</span>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="actions">
|
<main id="main">
|
||||||
<button id="refreshBtn" class="btn" onclick="fetchNewJoke()">
|
<section class="hero proof">
|
||||||
<span class="spinner" id="spinner"></span>
|
<div class="container hero__grid">
|
||||||
<span id="btnText">Tell Me Another Joke! 🚀</span>
|
<div class="hero__lead">
|
||||||
</button>
|
<h1>Programming Dad Joke</h1>
|
||||||
</div>
|
<p class="lead">Dry humor for engineers after hours, pulled live from your local model.</p>
|
||||||
|
<div class="actions">
|
||||||
|
<button id="refreshBtn" class="btn btn--primary crop" type="button" onclick="fetchNewJoke()">
|
||||||
|
<span class="spinner keep-motion" id="spinner" aria-hidden="true"></span>
|
||||||
|
<span id="btnText">Pull another joke</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="meta">
|
<div id="specimen" class="hero__stage">
|
||||||
<span>Host: <code>{{ host }}</code></span>
|
<article id="jokeSheet" class="sheet crop" aria-live="polite" aria-atomic="true">
|
||||||
<span>Model: <code>{{ model }}</code></span>
|
<div class="guide-bar" aria-hidden="true">
|
||||||
<span>Prompt: <code>"{{ prompt }}"</code></span>
|
<span class="guide-line"></span>
|
||||||
<span>App Version: <code>v{{ version }}</code></span>
|
<span class="guide-readout num">Hedvig Letters Serif, 25pt / 1.4</span>
|
||||||
|
</div>
|
||||||
|
<blockquote class="joke-specimen">
|
||||||
|
<p id="joke" class="joke-text">{{ joke }}</p>
|
||||||
|
</blockquote>
|
||||||
|
<div id="proofProgress" class="proof-progress" hidden>
|
||||||
|
<div class="proof-progress__meta">
|
||||||
|
<span id="progressStatus">Setting type on the press (30 to 50s typical)…</span>
|
||||||
|
<span id="progressReadout" class="num">0% (0s)</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="progressBar"
|
||||||
|
class="proof-progress__track"
|
||||||
|
role="progressbar"
|
||||||
|
aria-label="Joke generation progress"
|
||||||
|
aria-valuemin="0"
|
||||||
|
aria-valuemax="100"
|
||||||
|
aria-valuenow="0"
|
||||||
|
>
|
||||||
|
<div id="progressFill" class="proof-progress__fill"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="guide-bar" aria-hidden="true">
|
||||||
|
<span class="guide-readout num">Proofed on {{ model }}</span>
|
||||||
|
<span class="guide-line"></span>
|
||||||
|
<span class="guide-readout num">Release v{{ version }}</span>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</section>
|
||||||
</div>
|
|
||||||
|
<section class="section--notes" id="notes">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section__head">
|
||||||
|
<h2>Three rules on the press</h2>
|
||||||
|
<p class="lead">Every groaner on the sheet clears three checks before it leaves the studio.</p>
|
||||||
|
</div>
|
||||||
|
<ol class="set">
|
||||||
|
<li class="set__row">
|
||||||
|
<h3>Pun on the literal</h3>
|
||||||
|
<p>Take a noun every developer says ten times before lunch, such as cache, branch, or deadlock, and read it as plain kitchen English.</p>
|
||||||
|
</li>
|
||||||
|
<li class="set__row">
|
||||||
|
<h3>Keep the setup short</h3>
|
||||||
|
<p>One question across the counter. If the premise needs a whiteboard diagram, the punchline already missed its train.</p>
|
||||||
|
</li>
|
||||||
|
<li class="set__row">
|
||||||
|
<h3>Aim for the eye roll</h3>
|
||||||
|
<p>A proper dad joke never chases an applause break. It settles for a quiet sigh from across the table.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="site-footer" id="colophon">
|
||||||
|
<div class="container">
|
||||||
|
<div class="footer__head">
|
||||||
|
<h2>Press colophon</h2>
|
||||||
|
<p>Compiled on demand from your local inference runtime.</p>
|
||||||
|
</div>
|
||||||
|
<dl class="colophon-grid">
|
||||||
|
<div class="colophon-item">
|
||||||
|
<dt>Host</dt>
|
||||||
|
<dd class="num">{{ host }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="colophon-item">
|
||||||
|
<dt>Model</dt>
|
||||||
|
<dd class="num">{{ model }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="colophon-item">
|
||||||
|
<dt>Prompt</dt>
|
||||||
|
<dd>“{{ prompt }}”</dd>
|
||||||
|
</div>
|
||||||
|
<div class="colophon-item">
|
||||||
|
<dt>App Version</dt>
|
||||||
|
<dd class="num">v{{ version }}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
let progressTimer = null;
|
||||||
|
|
||||||
|
function computeExpectedProgress(elapsedSeconds) {
|
||||||
|
if (elapsedSeconds <= 0) return 0.02;
|
||||||
|
if (elapsedSeconds <= 30) {
|
||||||
|
return 0.02 + (elapsedSeconds / 30) * 0.66;
|
||||||
|
}
|
||||||
|
if (elapsedSeconds <= 50) {
|
||||||
|
return 0.68 + ((elapsedSeconds - 30) / 20) * 0.24;
|
||||||
|
}
|
||||||
|
return 0.92 + 0.06 * (1 - Math.exp(-(elapsedSeconds - 50) / 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProgressState(ratio, elapsedSeconds, statusText, isComplete) {
|
||||||
|
const progressBar = document.getElementById("progressBar");
|
||||||
|
const progressFill = document.getElementById("progressFill");
|
||||||
|
const progressReadout = document.getElementById("progressReadout");
|
||||||
|
const progressStatus = document.getElementById("progressStatus");
|
||||||
|
|
||||||
|
const clamped = Math.max(0, Math.min(1, ratio));
|
||||||
|
const pct = Math.round(clamped * 100);
|
||||||
|
const secs = isComplete
|
||||||
|
? Math.max(0.1, elapsedSeconds).toFixed(1)
|
||||||
|
: String(Math.max(0, Math.floor(elapsedSeconds)));
|
||||||
|
|
||||||
|
progressFill.style.setProperty("--progress", clamped.toFixed(4));
|
||||||
|
progressBar.setAttribute("aria-valuenow", String(pct));
|
||||||
|
progressReadout.textContent = pct + "% (" + secs + "s)";
|
||||||
|
if (statusText) {
|
||||||
|
progressStatus.textContent = statusText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchNewJoke() {
|
async function fetchNewJoke() {
|
||||||
const btn = document.getElementById("refreshBtn");
|
const btn = document.getElementById("refreshBtn");
|
||||||
const btnText = document.getElementById("btnText");
|
const btnText = document.getElementById("btnText");
|
||||||
const spinner = document.getElementById("spinner");
|
const spinner = document.getElementById("spinner");
|
||||||
const jokeEl = document.getElementById("joke");
|
const jokeEl = document.getElementById("joke");
|
||||||
|
const sheet = document.getElementById("jokeSheet");
|
||||||
|
const progressWrap = document.getElementById("proofProgress");
|
||||||
|
const progressFill = document.getElementById("progressFill");
|
||||||
|
|
||||||
|
if (progressTimer) {
|
||||||
|
clearInterval(progressTimer);
|
||||||
|
progressTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
|
btn.setAttribute("aria-busy", "true");
|
||||||
spinner.style.display = "inline-block";
|
spinner.style.display = "inline-block";
|
||||||
btnText.textContent = "Compiling humor...";
|
btnText.textContent = "Pulling fresh proof\u2026";
|
||||||
|
|
||||||
|
sheet.setAttribute("data-loading", "true");
|
||||||
|
progressWrap.hidden = false;
|
||||||
|
|
||||||
|
progressFill.style.transition = "none";
|
||||||
|
setProgressState(0, 0, "Setting type on the press (30 to 50s typical)\u2026", false);
|
||||||
|
void progressFill.offsetWidth;
|
||||||
|
progressFill.style.transition = "";
|
||||||
|
|
||||||
|
const startTime = performance.now();
|
||||||
|
setProgressState(0.02, 0, "Setting type on the press (30 to 50s typical)\u2026", false);
|
||||||
|
|
||||||
|
progressTimer = setInterval(() => {
|
||||||
|
const elapsed = (performance.now() - startTime) / 1000;
|
||||||
|
const ratio = computeExpectedProgress(elapsed);
|
||||||
|
const status =
|
||||||
|
elapsed < 30
|
||||||
|
? "Setting type on the press (30 to 50s typical)\u2026"
|
||||||
|
: elapsed <= 50
|
||||||
|
? "Inking the proof sheet (30 to 50s window)\u2026"
|
||||||
|
: "Finishing slow impression\u2026";
|
||||||
|
setProgressState(ratio, elapsed, status, false);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
let nextJoke = "";
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/joke");
|
const response = await fetch("/api/joke");
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("HTTP error " + response.status);
|
throw new Error("HTTP error " + response.status);
|
||||||
}
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
jokeEl.textContent = data.joke || "No joke returned.";
|
nextJoke = data.joke || "No joke returned.";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to load joke:", err);
|
console.error("Failed to load joke:", err);
|
||||||
jokeEl.textContent = "Oops! Could not fetch a new joke right now. Please try again.";
|
nextJoke = "Could not reach the joke service right now. Check the Ollama host and try again.";
|
||||||
} finally {
|
} finally {
|
||||||
|
if (progressTimer) {
|
||||||
|
clearInterval(progressTimer);
|
||||||
|
progressTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalElapsed = (performance.now() - startTime) / 1000;
|
||||||
|
setProgressState(1, totalElapsed, "Proof complete (30 to 50s typical)", true);
|
||||||
|
jokeEl.textContent = nextJoke;
|
||||||
|
sheet.removeAttribute("data-loading");
|
||||||
|
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
|
btn.removeAttribute("aria-busy");
|
||||||
spinner.style.display = "none";
|
spinner.style.display = "none";
|
||||||
btnText.textContent = "Tell Me Another Joke! 🚀";
|
btnText.textContent = "Pull another joke";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -375,6 +707,14 @@ def index():
|
||||||
version=version
|
version=version
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@app.route("/tokens.css")
|
||||||
|
def tokens_css():
|
||||||
|
return send_from_directory(os.path.dirname(os.path.abspath(__file__)), "tokens.css", mimetype="text/css")
|
||||||
|
|
||||||
|
@app.route("/base.css")
|
||||||
|
def base_css():
|
||||||
|
return send_from_directory(os.path.dirname(os.path.abspath(__file__)), "base.css", mimetype="text/css")
|
||||||
|
|
||||||
@app.route("/api/joke")
|
@app.route("/api/joke")
|
||||||
def api_joke():
|
def api_joke():
|
||||||
joke = request_joke()
|
joke = request_joke()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
*, *::before, *::after { box-sizing: border-box; }
|
||||||
|
html, body { overflow-x: clip; }
|
||||||
|
html { scrollbar-color: var(--color-muted) var(--color-paper); scroll-padding-top: var(--space-3xl); }
|
||||||
|
body { margin: 0; background: var(--color-paper); color: var(--color-ink); font-family: var(--font-body); font-size: var(--text-md); line-height: 1.55; -webkit-font-smoothing: antialiased; caret-color: var(--color-accent); }
|
||||||
|
::selection { background: color-mix(in oklab, var(--color-accent) 22%, transparent); color: var(--color-ink); }
|
||||||
|
:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
|
||||||
|
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.16em; }
|
||||||
|
h1, h2, h3 { margin: 0; font-family: var(--font-display); font-weight: var(--display-weight, 700); font-synthesis: none; letter-spacing: var(--tracking-display); line-height: 1.05; overflow-wrap: break-word; min-width: 0; text-wrap: balance; }
|
||||||
|
h1 { max-width: 20ch; font-size: var(--text-display); } h2 { max-width: 30ch; font-size: var(--text-section); } h3 { max-width: 40ch; font-size: var(--text-title); line-height: 1.2; }
|
||||||
|
.lead { font-size: var(--text-lead); line-height: 1.45; max-width: 52ch; }
|
||||||
|
p { margin: 0; text-wrap: pretty; }
|
||||||
|
p:not([class]) { max-width: 65ch; }
|
||||||
|
img, video { max-width: 100%; height: auto; display: block; }
|
||||||
|
svg { display: block; }
|
||||||
|
[hidden] { display: none !important; }
|
||||||
|
button, input, select, textarea { font: inherit; }
|
||||||
|
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); box-sizing: border-box; }
|
||||||
|
.num, .figure, td.num { font-variant-numeric: tabular-nums; }
|
||||||
|
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
||||||
|
[data-slot] { position: relative; overflow: hidden; background: linear-gradient(160deg, var(--color-paper-2, var(--color-paper)), var(--color-paper-3, var(--color-paper))); }
|
||||||
|
[data-slot] > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; }
|
||||||
|
[data-slot]:has(> img) > :not(img):not([data-keep]) { display: none !important; }
|
||||||
|
@media (max-width: 60rem) { :root { --gutter: 24px; --section-gap-s: 80px; --section-gap: 96px; --section-gap-l: 120px; } }
|
||||||
|
@media (max-width: 40rem) { :root { --gutter: 16px; --section-gap-s: 56px; --section-gap: 72px; --section-gap-l: 96px; --gap-columns: 16px; } }
|
||||||
|
@media (prefers-reduced-motion: reduce) { *:not(.keep-motion), *:not(.keep-motion)::before, *:not(.keep-motion)::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
:root {
|
||||||
|
color-scheme: light;
|
||||||
|
--space-3xs: 2px; --space-2xs: 4px; --space-xs: 8px; --space-sm: 12px; --space-md: 16px; --space-lg: 24px; --space-xl: 40px; --space-2xl: 64px; --space-3xl: 96px; --space-4xl: 144px;
|
||||||
|
--text-xs: 0.8125rem; --text-sm: 0.875rem; --text-md: 1rem; --text-lg: 1.25rem; --text-xl: 1.5625rem; --text-2xl: 1.953rem; --text-3xl: 2.441rem;
|
||||||
|
--text-display-s: clamp(2.25rem, 4vw + 0.5rem, 3.75rem); --text-display: clamp(2.75rem, 5vw + 1rem, 5.25rem);
|
||||||
|
--text-section: clamp(2rem, 2.5vw + 1rem, 3.5rem); --text-title: clamp(1.25rem, 0.75vw + 0.875rem, 1.625rem); --text-lead: clamp(1.125rem, 0.35vw + 1rem, 1.375rem);
|
||||||
|
--ease-out: cubic-bezier(0.16, 1, 0.3, 1); --ease-in: cubic-bezier(0.7, 0, 0.84, 0); --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
||||||
|
--dur-micro: 120ms; --dur-short: 220ms; --dur-long: 420ms;
|
||||||
|
--color-error: oklch(52% 0.19 27); --color-success: oklch(50% 0.12 150);
|
||||||
|
--container: 1440px; --gutter: 64px; --gap-columns: 32px; --section-gap-s: 120px; --section-gap: 144px; --section-gap-l: 176px;
|
||||||
|
--z-base: 1; --z-raised: 10; --z-dropdown: 100; --z-sticky: 200; --z-sticky-nav: 300; --z-modal: 400; --z-toast: 500; --z-tooltip: 600;
|
||||||
|
--color-paper: oklch(97.2% 0.006 215); --color-paper-2: oklch(94% 0.009 215); --color-paper-3: oklch(90.5% 0.011 215); --color-paper-hi: oklch(99% 0.004 215);
|
||||||
|
--color-ink: oklch(16% 0.032 250); --color-ink-2: oklch(34% 0.022 245); --color-muted: oklch(49% 0.018 240);
|
||||||
|
--color-rule: oklch(84% 0.010 225);
|
||||||
|
--color-accent: oklch(68% 0.172 50); --color-accent-ink: oklch(16% 0.032 250); --color-focus: oklch(52% 0.15 48);
|
||||||
|
--color-dot: color-mix(in oklab, var(--color-ink) 20%, transparent); --color-mark: color-mix(in oklab, var(--color-ink) 72%, transparent);
|
||||||
|
--font-display: "Hedvig Letters Serif", ui-serif, Georgia, serif; --font-body: "Geist", ui-sans-serif, system-ui, sans-serif;
|
||||||
|
--font-outlier: "Sofia Sans Extra Condensed", "Arial Narrow", ui-sans-serif, sans-serif;
|
||||||
|
--radius-card: 3px; --radius-pill: 3px; --rule-hair: 1px;
|
||||||
|
--shadow-card: none;
|
||||||
|
--shadow-sheet: 1px 2px 0 color-mix(in oklab, var(--color-ink) 7%, transparent), 10px 18px 36px -14px color-mix(in oklab, var(--color-ink) 32%, transparent);
|
||||||
|
--tracking-display: -0.022em; --display-weight: 400;
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
[metadata]
|
[metadata]
|
||||||
version = 0.1
|
version = 0.2
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue