Commit 5d9ce1dc by PLN (Algolia)

blog: hydra credit strip — CC badge icons, monochrome in every state, click-to-freeze

parent f76b0587
// Creative Commons license badges — official icon path data from the
// cc-icons project (npm cc-icons, OFL-1.1; the icon set itself is CC's
// trademark, used here to indicate an actual CC license). Vendored inline
// so the credit strip never makes a remote request, and tinted by CSS
// (fill: currentColor) to stay monochrome over any sketch.
// ViewBox 5.5 -3.5 64 64 for all four.
const VIEWBOX = '5.5 -3.5 64 64';
const PATHS = {
cc: [
,
],
by: [
'M 31.943359 0 C 23.104359 0 15.601688 3.0848125 9.4296875 9.2578125 C 3.1446875 15.657813 0 23.239 0 32 C 0 40.762 3.1446875 48.284312 9.4296875 54.570312 C 15.790688 60.856312 23.294359 64 31.943359 64 C 40.742359 64 48.379469 60.818078 54.855469 54.455078 C 60.951469 48.475078 64 40.991 64 32 C 64 23.048 60.893547 15.467813 54.685547 9.2578125 C 48.513547 3.0858125 40.931359 0 31.943359 0 z M 32.056641 5.7714844 C 39.294641 5.7714844 45.466313 8.3237344 50.570312 13.427734 C 55.673312 18.533734 58.226562 24.722047 58.226562 31.998047 C 58.226563 39.389047 55.712594 45.503797 50.683594 50.341797 C 45.388594 55.562797 39.177687 58.169922 32.054688 58.169922 C 24.892688 58.169922 18.722016 55.580438 13.541016 50.398438 C 8.3610156 45.220437 5.7714844 39.087953 5.7714844 32.001953 C 5.7714844 24.954953 8.3796094 18.763687 13.599609 13.429688 C 18.628609 8.3256875 24.780641 5.7714844 32.056641 5.7714844 z M 32 11.314453 C 29.027 11.314453 27.542969 12.820125 27.542969 15.828125 C 27.542969 18.799125 29.029 20.285156 32 20.285156 C 34.971 20.285156 36.457031 18.799125 36.457031 15.828125 C 36.457031 12.818125 34.973 11.314453 32 11.314453 z M 25.429688 22 C 24.859688 22 24.373656 22.200609 23.972656 22.599609 C 23.573656 22.999609 23.373047 23.484641 23.373047 24.056641 L 23.373047 37.142578 L 27.029297 37.142578 L 27.029297 52.683594 L 36.972656 52.683594 L 36.972656 37.142578 L 40.628906 37.142578 L 40.628906 24.056641 C 40.628906 23.485641 40.418953 22.999609 40.001953 22.599609 C 39.583953 22.199609 39.105266 22 38.572266 22 L 25.429688 22 z',
],
nc: [
'M 31.941406 0 C 23.103406 0 15.600688 3.0868125 9.4296875 9.2578125 C 3.1446875 15.657812 0 23.238 0 32 C 0 40.725 3.1446875 48.228625 9.4296875 54.515625 C 15.752687 60.838625 23.255406 64 31.941406 64 C 40.740406 64 48.380469 60.819031 54.855469 54.457031 C 60.952469 48.475031 64 40.992 64 32 C 64 23.009 60.893547 15.427859 54.685547 9.2558594 C 48.513547 3.0848594 40.931406 0 31.941406 0 z M 32.056641 5.7148438 C 39.330641 5.7148437 45.505266 8.2847344 50.572266 13.427734 C 55.676266 18.457734 58.228516 24.648953 58.228516 32.001953 C 58.228516 34.325953 57.962734 36.609422 57.427734 38.857422 L 41.029297 31.544922 L 35.886719 29.259766 L 33.085938 28.001953 L 29.029297 26.171875 C 28.839297 25.904875 28.744141 25.638094 28.744141 25.371094 C 28.744141 24.457094 29.135016 23.810688 29.916016 23.429688 C 30.696016 23.049688 31.581266 22.857422 32.572266 22.857422 C 34.439266 22.857422 36.265594 23.485187 38.058594 24.742188 L 42.230469 20.457031 C 39.830469 18.629031 37.164469 17.639328 34.230469 17.486328 L 34.230469 12.570312 L 30.515625 12.570312 L 30.515625 17.486328 C 28.533625 17.601328 26.798453 18.162875 25.314453 19.171875 C 23.827453 20.180875 22.818156 21.523219 22.285156 23.199219 L 10.056641 17.714844 C 11.160641 16.113844 12.341609 14.685734 13.599609 13.427734 C 18.628609 8.2847344 24.780641 5.7148438 32.056641 5.7148438 z M 7.1992188 23.371094 L 23.542969 30.683594 L 28.628906 32.912109 L 32.400391 34.626953 L 35.484375 35.941406 C 36.055375 36.436406 36.34375 37.083766 36.34375 37.884766 C 36.34375 39.029766 35.951875 39.847797 35.171875 40.341797 C 34.389875 40.838797 33.445797 41.085938 32.341797 41.085938 C 29.674797 41.085938 27.291266 40.113875 25.197266 38.171875 L 20.798828 42.628906 C 23.730828 45.143906 26.968719 46.419078 30.511719 46.455078 L 30.511719 51.371094 L 34.226562 51.371094 L 34.226562 46.455078 C 36.435563 46.266078 38.348703 45.531859 39.970703 44.255859 C 41.588703 42.980859 42.606344 41.334453 43.027344 39.314453 L 55.082031 44.685547 C 54.016031 46.630547 52.552547 48.497109 50.685547 50.287109 C 45.464547 55.508109 39.255688 58.115234 32.054688 58.115234 C 24.931687 58.115234 18.760016 55.544391 13.541016 50.400391 C 8.3610156 45.220391 5.7714844 39.087953 5.7714844 32.001953 C 5.7714844 28.877953 6.2472188 25.999094 7.1992188 23.371094 z',
],
sa: [
'M 31.943359 0 C 23.143359 0 15.638687 3.1054062 9.4296875 9.3164062 C 3.1446875 15.716406 0 23.278953 0 32.001953 C 0 40.764953 3.1446875 48.285313 9.4296875 54.570312 C 15.790688 60.856313 23.294359 64 31.943359 64 C 40.779359 64 48.418469 60.837672 54.855469 54.513672 C 60.951469 48.456672 64 40.954 64 32 C 64 23.048 60.893547 15.486453 54.685547 9.3144531 C 48.474547 3.1044531 40.894359 0 31.943359 0 z M 32.056641 5.7714844 C 39.331641 5.7714844 45.503312 8.3413281 50.570312 13.486328 C 55.673312 18.514328 58.226562 24.687 58.226562 32 C 58.226563 39.353 55.712594 45.46775 50.683594 50.34375 C 45.388594 55.56275 39.177687 58.171875 32.054688 58.171875 C 24.892688 58.171875 18.722016 55.582391 13.541016 50.400391 C 8.3610156 45.257391 5.7714844 39.125 5.7714844 32 C 5.7714844 24.954 8.3796094 18.782328 13.599609 13.486328 C 18.628609 8.3433281 24.780641 5.7714844 32.056641 5.7714844 z M 31.826172 15.142578 C 28.168172 15.142578 25.065672 16.219094 22.513672 18.371094 C 19.960672 20.523094 18.380484 23.560375 17.771484 27.484375 L 15.259766 27.484375 L 21.601562 33.826172 L 27.943359 27.484375 L 25.601562 27.484375 C 26.134563 23.713375 28.532781 21.828125 32.800781 21.828125 C 35.164781 21.828125 36.972469 22.713375 38.230469 24.484375 C 39.486469 26.256375 40.115234 28.837516 40.115234 32.228516 C 40.115234 35.465516 39.429641 38.056047 38.056641 39.998047 C 36.685641 41.940047 34.874906 42.912109 32.628906 42.912109 C 28.132906 42.912109 25.789609 40.969984 25.599609 37.083984 L 17.541016 37.083984 C 18.075016 41.083984 19.654203 44.169797 22.283203 46.341797 C 24.911203 48.513797 28.035297 49.597656 31.654297 49.597656 C 36.570297 49.597656 40.56825 47.968891 43.65625 44.712891 C 46.74025 41.456891 48.283203 37.370078 48.283203 32.455078 C 48.283203 27.389078 46.797172 23.236047 43.826172 19.998047 C 40.855172 16.762047 36.853172 15.142578 31.826172 15.142578 z',
],
};
/** One CC badge circle — `kind` is 'cc' | 'by' | 'nc' | 'sa'. */
export default function CcIcon({ kind, size = 14 }) {
return (
<svg viewBox={VIEWBOX} width={size} height={size} fill="currentColor" aria-hidden="true">
{PATHS[kind].map((d) => (
<path key={d.slice(0, 24)} d={d} />
))}
</svg>
);
}
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import styles from './blog.module.css'; import styles from './blog.module.css';
import CcIcon from './CcIcon';
/** /**
* A Hydra sketch island at the opening/closing of a post. It boots on page * A Hydra sketch island at the opening/closing of a post. It boots on page
...@@ -16,11 +17,32 @@ import styles from './blog.module.css'; ...@@ -16,11 +17,32 @@ import styles from './blog.module.css';
* hydra-synth instance; the last one booted owns the global generators, * hydra-synth instance; the last one booted owns the global generators,
* which is the right trade for a page that books both ends. * which is the right trade for a page that books both ends.
*/ */
/** The CC badge circles a license name declares: 'CC BY-NC-SA 4.0' → cc, by, nc, sa. */
function CcBadges({ license }) {
const kinds = ['cc'];
for (const part of license.split(/\s+/).slice(1)) {
for (const token of part.split('-')) {
const kind = token.toLowerCase();
if (['by', 'nc', 'sa', 'nd'].includes(kind)) kinds.push(kind);
}
}
return (
<span className={styles.hydraBadges} aria-hidden="true">
{kinds.map((kind) => (
<CcIcon key={kind} kind={kind} />
))}
</span>
);
}
export default function HydraEmbed({ sketch, lang }) { export default function HydraEmbed({ sketch, lang }) {
const [running, setRunning] = useState(false); const [running, setRunning] = useState(false);
const [paused, setPaused] = useState(false);
const [failed, setFailed] = useState(false); const [failed, setFailed] = useState(false);
const canvasRef = useRef(null); const canvasRef = useRef(null);
const stageRef = useRef(null); const stageRef = useRef(null);
const hydraRef = useRef(null);
const savedSpeedRef = useRef(1);
useEffect(() => { useEffect(() => {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
...@@ -40,6 +62,7 @@ export default function HydraEmbed({ sketch, lang }) { ...@@ -40,6 +62,7 @@ export default function HydraEmbed({ sketch, lang }) {
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
hydra = new Hydra({ canvas, detectAudio: false, width, height }); hydra = new Hydra({ canvas, detectAudio: false, width, height });
hydraRef.current = hydra;
hydra.eval(sketch.source); hydra.eval(sketch.source);
}) })
.catch(() => { .catch(() => {
...@@ -47,18 +70,47 @@ export default function HydraEmbed({ sketch, lang }) { ...@@ -47,18 +70,47 @@ export default function HydraEmbed({ sketch, lang }) {
}); });
return () => { return () => {
cancelled = true; cancelled = true;
hydraRef.current = null;
if (hydra && typeof hydra.close === 'function') hydra.close(); if (hydra && typeof hydra.close === 'function') hydra.close();
}; };
}, [running, sketch.source]); }, [running, sketch.source]);
/** Click on a running sketch freezes time (speed → 0); click again resumes. */
const togglePause = () => {
const hydra = hydraRef.current;
if (!hydra) return;
if (paused) {
hydra.sandbox.set('speed', savedSpeedRef.current);
setPaused(false);
} else {
savedSpeedRef.current = hydra.synth.speed || 1;
hydra.sandbox.set('speed', 0);
setPaused(true);
}
};
const hint = lang === 'en' ? 'Run the sketch' : 'Lancer le sketch'; const hint = lang === 'en' ? 'Run the sketch' : 'Lancer le sketch';
const codeLabel = lang === 'en' ? 'sketch source' : 'source du sketch'; const codeLabel = lang === 'en' ? 'sketch source' : 'code source';
return ( return (
<figure className={styles.hydra}> <figure className={styles.hydra}>
<div ref={stageRef} className={styles.hydraStage}> <div ref={stageRef} className={styles.hydraStage}>
{running ? ( {running ? (
<canvas ref={canvasRef} className={styles.hydraCanvas} /> <canvas
ref={canvasRef}
className={styles.hydraCanvas}
onClick={togglePause}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
togglePause();
}
}}
aria-label={paused ? (lang === 'en' ? 'Resume the sketch' : 'Relancer le sketch') : lang === 'en' ? 'Freeze the sketch' : 'Figer le sketch'}
title={paused ? (lang === 'en' ? 'Click to resume' : 'Cliquer pour relancer') : lang === 'en' ? 'Click to freeze' : 'Cliquer pour figer'}
/>
) : ( ) : (
<a <a
href={sketch.url} href={sketch.url}
...@@ -81,11 +133,20 @@ export default function HydraEmbed({ sketch, lang }) { ...@@ -81,11 +133,20 @@ export default function HydraEmbed({ sketch, lang }) {
{sketch.author && <span className={styles.hydraAuthor}>{sketch.author}</span>} {sketch.author && <span className={styles.hydraAuthor}>{sketch.author}</span>}
{sketch.license && {sketch.license &&
(sketch.licenseUrl ? ( (sketch.licenseUrl ? (
<a href={sketch.licenseUrl} target="_blank" rel="license noopener noreferrer"> <a
href={sketch.licenseUrl}
target="_blank"
rel="license noopener noreferrer"
className={styles.hydraLicense}
>
<CcBadges license={sketch.license} />
{sketch.license} {sketch.license}
</a> </a>
) : ( ) : (
<span>{sketch.license}</span> <span className={styles.hydraLicense}>
<CcBadges license={sketch.license} />
{sketch.license}
</span>
))} ))}
<a href={sketch.url} target="_blank" rel="noopener noreferrer"> <a href={sketch.url} target="_blank" rel="noopener noreferrer">
hydra.ojack.xyz ↗ hydra.ojack.xyz ↗
......
...@@ -637,6 +637,7 @@ ...@@ -637,6 +637,7 @@
} }
.hydraCanvas { .hydraCanvas {
background: #000; background: #000;
cursor: pointer;
} }
.hydraFacade { .hydraFacade {
display: flex; display: flex;
...@@ -677,12 +678,16 @@ ...@@ -677,12 +678,16 @@
padding: 0.4rem 0.9rem; padding: 0.4rem 0.9rem;
font-size: 0.78rem; font-size: 0.78rem;
/* Strictly monochrome — this strip floats over the sketch's output, so /* Strictly monochrome — this strip floats over the sketch's output, so
theme tokens (which flip with the site's light/dark) would betray it. */ theme tokens (which flip with the site's light/dark) would betray it.
Two classes to out-rank main.css's `.site-shell footer` color. */
color: rgba(255, 255, 255, 0.85); color: rgba(255, 255, 255, 0.85);
background: linear-gradient(transparent, rgba(0, 0, 0, 0.78)); background: linear-gradient(transparent, rgba(0, 0, 0, 0.78));
opacity: 0; opacity: 0;
transition: opacity 0.2s ease; transition: opacity 0.2s ease;
} }
.hydra .hydraFooter {
color: rgba(255, 255, 255, 0.85);
}
.hydraStage:hover .hydraFooter, .hydraStage:hover .hydraFooter,
.hydraStage:focus-within .hydraFooter { .hydraStage:focus-within .hydraFooter {
opacity: 1; opacity: 1;
...@@ -700,10 +705,30 @@ ...@@ -700,10 +705,30 @@
text-decoration: underline; text-decoration: underline;
text-underline-offset: 2px; text-underline-offset: 2px;
} }
.hydraFooter a:visited, /* Monochrome in every state, at raised specificity: the global sheets set
.hydraFooter a:hover { link colors on a/`a:hover` at (0,1,1) and load after modules, so an equal
specificity here loses the moment the reader hovers — which is exactly
when this strip is visible. */
.hydra .hydraFooter a:link,
.hydra .hydraFooter a:visited,
.hydra .hydraFooter a:hover,
.hydra .hydraFooter a:focus-visible,
.hydra .hydraFooter a:active {
color: rgba(255, 255, 255, 0.85); color: rgba(255, 255, 255, 0.85);
} }
.hydraLicense {
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.hydraBadges {
display: inline-flex;
align-items: center;
gap: 2px;
}
.hydraBadges svg {
display: block;
}
.hydraCode { .hydraCode {
margin-top: 0.4rem; margin-top: 0.4rem;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment