diff --git a/assets/icons/close.svg b/assets/icons/close.svg
new file mode 100644
index 0000000..ae3fbc7
--- /dev/null
+++ b/assets/icons/close.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/index.html b/index.html
index fadc28f..1414388 100644
--- a/index.html
+++ b/index.html
@@ -11,7 +11,7 @@
@@ -22,7 +22,7 @@
@@ -35,7 +35,7 @@
@@ -46,7 +46,7 @@
"@type": "WebApplication",
"name": "Fleeting Garden",
"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",
"applicationCategory": "DesignApplication",
"operatingSystem": "Any",
@@ -118,35 +118,47 @@
id="info-panel"
class="hidden info-page"
role="region"
- aria-label="About panel"
+ aria-labelledby="info-panel-title"
aria-hidden="true"
tabindex="-1"
inert
>
-
- Fleeting Garden
-
+
+
+
+
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,
branch off, and slowly rewrite the patch you laid down into something you
didn't quite plan.
-
- Three swatches plant the line. The eraser carves a clearing. The mirror folds
- one gesture into many, like footpaths around a hidden well.
-
-
- Switch vibes to change the season; your shapes stay, the light moves. Add or
- quiet the piano. Restart when you want a fresh field. Take a snapshot if you
- want to keep one particular instant of weather.
-
-
+
+
+ Three swatches plant the line; the eraser carves a clearing.
+ The mirror folds one gesture into many.
+ The arrows change the season.
+
+
+
Built with WebGPU, running locally in your browser. More of my work at
schmelczer.dev .
-
+
diff --git a/src/config.ts b/src/config.ts
index e0e6b19..06d2309 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -22,7 +22,7 @@ export const appConfig = {
audio: createGardenAudioConfig(),
analytics: {
autoCapturePageviews: true,
- domain: 'fleeting.garden',
+ domain: 'schmelczer.dev/fleeting',
endpoint: 'https://stats.schmelczer.dev/status',
logging: import.meta.env.DEV,
},
diff --git a/src/index.ts b/src/index.ts
index 74b98d7..79b5251 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -71,6 +71,10 @@ const main = async () => {
HTMLButtonElement
);
const infoButton = queryRequiredElement('[data-control="info"]', HTMLButtonElement);
+ const infoCloseButton = queryRequiredElement(
+ '[data-control="info-close"]',
+ HTMLButtonElement
+ );
const infoElement = queryRequiredElement('.info-page', HTMLElement);
const fullScreenButton = queryRequiredElement(
'[data-control="full-screen"]',
@@ -113,6 +117,7 @@ const main = async () => {
};
const infoPageHandler = new CollapsiblePanelAnimator(infoButton, infoElement, aside);
+ infoCloseButton.addEventListener('click', () => infoPageHandler.close());
new MenuHider(
aside,
() =>
diff --git a/src/style/_panels.scss b/src/style/_panels.scss
index 28717bb..3e92b02 100644
--- a/src/style/_panels.scss
+++ b/src/style/_panels.scss
@@ -1,24 +1,28 @@
@use 'mixins' as *;
html > body > aside.control-dock > .info-page {
- width: min(calc(100vw - 1rem), 560px);
- max-height: min(58vh, 520px);
- max-height: min(58dvh, 520px);
+ width: min(100%, 520px);
+ max-height: min(62vh, 480px);
+ max-height: min(62dvh, 480px);
margin: 0 auto 10px;
overflow-x: hidden;
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;
background:
- linear-gradient(180deg, rgb(255 255 255 / 97%), rgb(243 247 239 / 96%)),
- rgb(255 255 255);
- color: rgb(24 30 27);
+ linear-gradient(180deg, rgb(252 255 249 / 94%), rgb(234 241 232 / 91%)),
+ rgb(249 252 247 / 92%);
+ color: rgb(18 28 24);
box-shadow:
- 0 20px 54px rgb(0 0 0 / 38%),
- 0 2px 12px rgb(0 0 0 / 22%);
- backdrop-filter: blur(12px);
+ 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%);
+ backdrop-filter: blur(16px) saturate(118%);
scrollbar-width: thin;
- scrollbar-color: var(--main-color) transparent;
+ scrollbar-color: rgb(69 98 88 / 62%) transparent;
+ -webkit-overflow-scrolling: touch;
transition:
max-height var(--transition-time-long),
opacity var(--transition-time-long),
@@ -32,45 +36,179 @@ html > body > aside.control-dock > .info-page {
}
&::-webkit-scrollbar-thumb {
- background-color: var(--main-color);
+ background-color: rgb(69 98 88 / 62%);
border-radius: 8px;
}
&:focus-visible {
- outline: 2px solid white;
+ outline: 2px solid rgb(17 56 45);
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;
flex-direction: column;
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;
+ .info-page__header {
+ 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;
}
- p {
- max-width: 54ch;
- margin-bottom: 0;
- color: rgb(42 48 45);
- font-size: 1.1rem;
- line-height: 1.65;
- hyphens: auto;
+ &:hover {
+ border-color: rgb(34 57 50 / 30%);
+ background: rgb(255 255 255 / 64%);
+ transform: translateY(-1px);
}
- a {
- color: rgb(0 84 120);
- font-weight: 400;
+ &:focus-visible {
+ outline: 2px solid rgb(17 56 45);
+ outline-offset: 2px;
+ box-shadow: 0 0 0 4px rgb(255 255 255 / 72%);
+ }
+ }
- &:focus-visible {
- outline: 2px solid currentColor;
- outline-offset: 3px;
- }
+ .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;
+ 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 {
+ color: rgb(0 83 105);
+ font-weight: 700;
+ text-decoration-color: rgb(0 83 105 / 34%);
+ text-underline-offset: 0.18em;
+
+ &:hover {
+ text-decoration-color: currentColor;
+ }
+
+ &:focus-visible {
+ outline: 2px solid currentColor;
+ outline-offset: 3px;
}
}
@@ -81,16 +219,51 @@ html > body > aside.control-dock > .info-page {
opacity: 0;
pointer-events: none;
box-shadow: none;
- transform: translateY(8px);
+ transform: translateY(6px) scale(0.985);
visibility: hidden;
}
@include on-small-screen {
- max-height: min(54vh, 500px);
- max-height: min(54dvh, 500px);
+ width: min(100%, 520px);
+ max-height: min(58vh, 500px);
+ max-height: min(58dvh, 500px);
- > section {
- padding: var(--small-margin);
+ .info-page__content {
+ 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)
+ )
+ )
+ );
+ }
}