Update description

This commit is contained in:
Andras Schmelczer 2026-05-24 21:00:03 +01:00
parent f863588060
commit 79638d5fa4
5 changed files with 254 additions and 59 deletions

5
assets/icons/close.svg Normal file
View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M6.4 5 12 10.6 17.6 5 19 6.4 13.4 12 19 17.6 17.6 19 12 13.4 6.4 19 5 17.6 10.6 12 5 6.4z"
/>
</svg>

After

Width:  |  Height:  |  Size: 179 B

View file

@ -11,7 +11,7 @@
<meta name="author" content="Andras Schmelczer" /> <meta name="author" content="Andras Schmelczer" />
<meta <meta
name="description" name="description"
content="Tend it while you can. The garden returns to weather either way. A WebGPU drawing toy in your browser." content="Plant colour, fold gestures with mirrors, and watch small agents turn each brushstroke into a shifting WebGPU garden."
/> />
<link rel="canonical" href="https://schmelczer.dev/fleeting/" /> <link rel="canonical" href="https://schmelczer.dev/fleeting/" />
@ -22,7 +22,7 @@
<meta property="og:locale" content="en_US" /> <meta property="og:locale" content="en_US" />
<meta <meta
property="og:description" property="og:description"
content="Tend it while you can. The garden returns to weather either way. A WebGPU drawing toy in your browser." content="Plant colour, fold gestures with mirrors, and watch small agents turn each brushstroke into a shifting WebGPU garden."
/> />
<meta property="og:url" content="https://schmelczer.dev/fleeting/" /> <meta property="og:url" content="https://schmelczer.dev/fleeting/" />
<meta property="og:image" content="https://schmelczer.dev/fleeting/og-image.jpg" /> <meta property="og:image" content="https://schmelczer.dev/fleeting/og-image.jpg" />
@ -35,7 +35,7 @@
<meta name="twitter:title" content="Fleeting Garden" /> <meta name="twitter:title" content="Fleeting Garden" />
<meta <meta
name="twitter:description" name="twitter:description"
content="Tend it while you can. The garden returns to weather either way. A WebGPU drawing toy in your browser." content="Plant colour, fold gestures with mirrors, and watch small agents turn each brushstroke into a shifting WebGPU garden."
/> />
<meta name="twitter:image" content="https://schmelczer.dev/fleeting/og-image.jpg" /> <meta name="twitter:image" content="https://schmelczer.dev/fleeting/og-image.jpg" />
<meta name="twitter:image:alt" content="Fleeting Garden social preview image." /> <meta name="twitter:image:alt" content="Fleeting Garden social preview image." />
@ -46,7 +46,7 @@
"@type": "WebApplication", "@type": "WebApplication",
"name": "Fleeting Garden", "name": "Fleeting Garden",
"url": "https://schmelczer.dev/fleeting/", "url": "https://schmelczer.dev/fleeting/",
"description": "Tend it while you can. The garden returns to weather either way. A WebGPU drawing toy in your browser.", "description": "Plant colour, fold gestures with mirrors, and watch small agents turn each brushstroke into a shifting WebGPU garden.",
"image": "https://schmelczer.dev/fleeting/og-image.jpg", "image": "https://schmelczer.dev/fleeting/og-image.jpg",
"applicationCategory": "DesignApplication", "applicationCategory": "DesignApplication",
"operatingSystem": "Any", "operatingSystem": "Any",
@ -118,35 +118,47 @@
id="info-panel" id="info-panel"
class="hidden info-page" class="hidden info-page"
role="region" role="region"
aria-label="About panel" aria-labelledby="info-panel-title"
aria-hidden="true" aria-hidden="true"
tabindex="-1" tabindex="-1"
inert inert
> >
<section> <div class="info-page__content">
<h1>Fleeting Garden</h1> <header class="info-page__header">
<p> <span class="info-page__mark" aria-hidden="true"></span>
<div class="info-page__heading">
<p class="info-page__eyebrow">About</p>
<h2 id="info-panel-title">Fleeting Garden</h2>
</div>
<button
class="info-page__close"
data-control="info-close"
type="button"
aria-label="Close about panel"
title="Close"
></button>
</header>
<p class="info-page__lede">
A garden is what we tend; the wild is what we get the moment we look away. A garden is what we tend; the wild is what we get the moment we look away.
Both happen here at once. Your strokes plant colour, small agents follow them, Both happen here at once. Your strokes plant colour, small agents follow them,
branch off, and slowly rewrite the patch you laid down into something you branch off, and slowly rewrite the patch you laid down into something you
didn't quite plan. didn't quite plan.
</p> </p>
<p>
Three swatches plant the line. The eraser carves a clearing. The mirror folds <ul class="info-page__notes">
one gesture into many, like footpaths around a hidden well. <li>Three swatches plant the line; the eraser carves a clearing.</li>
</p> <li>The mirror folds one gesture into many.</li>
<p> <li>The arrows change the season.</li>
Switch vibes to change the season; your shapes stay, the light moves. Add or </ul>
quiet the piano. Restart when you want a fresh field. Take a snapshot if you
want to keep one particular instant of weather. <p class="info-page__meta">
</p>
<p>
Built with WebGPU, running locally in your browser. More of my work at Built with WebGPU, running locally in your browser. More of my work at
<a href="https://schmelczer.dev" target="_blank" rel="noopener" <a href="https://schmelczer.dev" target="_blank" rel="noopener"
>schmelczer.dev</a >schmelczer.dev</a
>. >.
</p> </p>
</section> </div>
</section> </section>
<div class="toolbar-row" role="toolbar" aria-label="Garden toolbar"> <div class="toolbar-row" role="toolbar" aria-label="Garden toolbar">

View file

@ -22,7 +22,7 @@ export const appConfig = {
audio: createGardenAudioConfig(), audio: createGardenAudioConfig(),
analytics: { analytics: {
autoCapturePageviews: true, autoCapturePageviews: true,
domain: 'fleeting.garden', domain: 'schmelczer.dev/fleeting',
endpoint: 'https://stats.schmelczer.dev/status', endpoint: 'https://stats.schmelczer.dev/status',
logging: import.meta.env.DEV, logging: import.meta.env.DEV,
}, },

View file

@ -71,6 +71,10 @@ const main = async () => {
HTMLButtonElement HTMLButtonElement
); );
const infoButton = queryRequiredElement('[data-control="info"]', HTMLButtonElement); const infoButton = queryRequiredElement('[data-control="info"]', HTMLButtonElement);
const infoCloseButton = queryRequiredElement(
'[data-control="info-close"]',
HTMLButtonElement
);
const infoElement = queryRequiredElement('.info-page', HTMLElement); const infoElement = queryRequiredElement('.info-page', HTMLElement);
const fullScreenButton = queryRequiredElement( const fullScreenButton = queryRequiredElement(
'[data-control="full-screen"]', '[data-control="full-screen"]',
@ -113,6 +117,7 @@ const main = async () => {
}; };
const infoPageHandler = new CollapsiblePanelAnimator(infoButton, infoElement, aside); const infoPageHandler = new CollapsiblePanelAnimator(infoButton, infoElement, aside);
infoCloseButton.addEventListener('click', () => infoPageHandler.close());
new MenuHider( new MenuHider(
aside, aside,
() => () =>

View file

@ -1,24 +1,28 @@
@use 'mixins' as *; @use 'mixins' as *;
html > body > aside.control-dock > .info-page { html > body > aside.control-dock > .info-page {
width: min(calc(100vw - 1rem), 560px); width: min(100%, 520px);
max-height: min(58vh, 520px); max-height: min(62vh, 480px);
max-height: min(58dvh, 520px); max-height: min(62dvh, 480px);
margin: 0 auto 10px; margin: 0 auto 10px;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
border: 1px solid rgb(255 255 255 / 78%); overscroll-behavior: contain;
touch-action: pan-y;
border: 1px solid rgb(255 255 255 / 46%);
border-radius: 8px; border-radius: 8px;
background: background:
linear-gradient(180deg, rgb(255 255 255 / 97%), rgb(243 247 239 / 96%)), linear-gradient(180deg, rgb(252 255 249 / 94%), rgb(234 241 232 / 91%)),
rgb(255 255 255); rgb(249 252 247 / 92%);
color: rgb(24 30 27); color: rgb(18 28 24);
box-shadow: box-shadow:
0 20px 54px rgb(0 0 0 / 38%), inset 0 1px 0 rgb(255 255 255 / 58%),
0 2px 12px rgb(0 0 0 / 22%); 0 16px 42px rgb(0 0 0 / 30%),
backdrop-filter: blur(12px); 0 2px 10px rgb(0 0 0 / 18%);
backdrop-filter: blur(16px) saturate(118%);
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: var(--main-color) transparent; scrollbar-color: rgb(69 98 88 / 62%) transparent;
-webkit-overflow-scrolling: touch;
transition: transition:
max-height var(--transition-time-long), max-height var(--transition-time-long),
opacity var(--transition-time-long), opacity var(--transition-time-long),
@ -32,47 +36,181 @@ html > body > aside.control-dock > .info-page {
} }
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
background-color: var(--main-color); background-color: rgb(69 98 88 / 62%);
border-radius: 8px; border-radius: 8px;
} }
&:focus-visible { &:focus-visible {
outline: 2px solid white; outline: 2px solid rgb(17 56 45);
outline-offset: 3px; outline-offset: 3px;
box-shadow:
0 0 0 5px rgb(255 255 255 / 68%),
inset 0 1px 0 rgb(255 255 255 / 58%),
0 16px 42px rgb(0 0 0 / 30%),
0 2px 10px rgb(0 0 0 / 18%);
} }
> section { .info-page__content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.85rem; gap: 0.85rem;
padding: var(--normal-margin); padding: 18px 20px 16px;
h1 {
margin-bottom: 0;
color: rgb(16 24 20);
font-size: 2rem;
line-height: 1.1;
} }
p { .info-page__header {
max-width: 54ch; display: flex;
align-items: center;
gap: 0.7rem;
padding-bottom: 0.1rem;
}
.info-page__heading {
min-width: 0;
}
.info-page__mark {
display: grid;
flex: 0 0 auto;
place-items: center;
width: 1.8rem;
height: 1.8rem;
border: 1px solid rgb(42 74 65 / 24%);
border-radius: 8px;
background:
linear-gradient(180deg, rgb(255 255 255 / 74%), rgb(217 231 222 / 70%)),
rgb(232 240 235);
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 76%),
0 5px 14px rgb(21 44 39 / 12%);
&::before {
content: '';
width: 1rem;
height: 1rem;
background: rgb(19 57 48);
mask: url('../../assets/icons/info.svg') center / contain no-repeat;
}
}
.info-page__close {
position: relative;
flex: 0 0 auto;
width: 2rem;
height: 2rem;
margin-left: auto;
border: 1px solid rgb(34 57 50 / 18%);
border-radius: 8px;
background: rgb(255 255 255 / 36%);
cursor: pointer;
transition:
background-color var(--transition-time),
border-color var(--transition-time),
transform var(--transition-time);
&::before {
content: '';
position: absolute;
inset: 0;
width: 0.9rem;
height: 0.9rem;
margin: auto;
background: rgb(28 45 39);
mask: url('../../assets/icons/close.svg') center / contain no-repeat;
}
&:hover {
border-color: rgb(34 57 50 / 30%);
background: rgb(255 255 255 / 64%);
transform: translateY(-1px);
}
&:focus-visible {
outline: 2px solid rgb(17 56 45);
outline-offset: 2px;
box-shadow: 0 0 0 4px rgb(255 255 255 / 72%);
}
}
.info-page__eyebrow {
margin-bottom: 0.12rem;
color: rgb(73 91 85);
font-size: 0.68rem;
font-weight: 700;
line-height: 1.2;
letter-spacing: 0;
text-transform: uppercase;
}
h2 {
overflow-wrap: break-word;
margin-bottom: 0;
color: rgb(9 21 17);
font-size: 1.12rem;
font-weight: 700;
line-height: 1.18;
}
.info-page__lede,
.info-page__notes,
.info-page__meta {
max-width: 56ch;
overflow-wrap: break-word;
color: rgb(25 35 32);
font-size: 0.95rem;
line-height: 1.56;
}
.info-page__lede,
.info-page__meta {
margin-bottom: 0; margin-bottom: 0;
color: rgb(42 48 45);
font-size: 1.1rem;
line-height: 1.65;
hyphens: auto; hyphens: auto;
} }
.info-page__notes {
display: grid;
gap: 0.45rem;
margin: 0.1rem 0;
padding-left: 1.1rem;
list-style: disc;
li {
padding-left: 0.1rem;
}
li::marker {
color: rgb(25 108 82);
font-size: 0.85em;
}
}
.info-page__meta {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
align-items: baseline;
margin-top: 0.2rem;
padding-top: 0.75rem;
border-top: 1px solid rgb(43 66 57 / 16%);
color: rgb(67 82 77);
font-size: 0.8rem;
line-height: 1.45;
}
a { a {
color: rgb(0 84 120); color: rgb(0 83 105);
font-weight: 400; font-weight: 700;
text-decoration-color: rgb(0 83 105 / 34%);
text-underline-offset: 0.18em;
&:hover {
text-decoration-color: currentColor;
}
&:focus-visible { &:focus-visible {
outline: 2px solid currentColor; outline: 2px solid currentColor;
outline-offset: 3px; outline-offset: 3px;
} }
} }
}
&.hidden { &.hidden {
max-height: 0; max-height: 0;
@ -81,16 +219,51 @@ html > body > aside.control-dock > .info-page {
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
box-shadow: none; box-shadow: none;
transform: translateY(8px); transform: translateY(6px) scale(0.985);
visibility: hidden; visibility: hidden;
} }
@include on-small-screen { @include on-small-screen {
max-height: min(54vh, 500px); width: min(100%, 520px);
max-height: min(54dvh, 500px); max-height: min(58vh, 500px);
max-height: min(58dvh, 500px);
> section { .info-page__content {
padding: var(--small-margin); gap: 0.75rem;
padding: 14px;
} }
.info-page__lede,
.info-page__notes {
font-size: 0.95rem;
line-height: 1.52;
}
.info-page__meta {
font-size: 0.875rem;
line-height: 1.45;
}
}
@media (max-height: 420px) {
max-height: min(
58vh,
max(
10rem,
calc(
100vh - 168px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)
)
)
);
max-height: min(
58dvh,
max(
10rem,
calc(
100dvh -
168px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)
)
)
);
} }
} }