Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/bench/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
<div class="header-row">
<div class="flex items-center gap-2">
<a href="https://keyrove.pages.dev/" class="wordmark"
><span data-icon="keyboard" class="size-6"></span>keyrove</a
>
><span data-icon="wordmark" class="h-6"></span
></a>
<span class="bench-crumb">bench</span>
</div>
<div class="flex items-center gap-1 sm:gap-4">
Expand Down Expand Up @@ -292,8 +292,8 @@ <h2 id="method-heading" class="method-heading">How it is measured</h2>
<div class="footer-row">
<p class="footer-meta">
<a href="https://keyrove.pages.dev/" class="wordmark"
><span data-icon="keyboard" class="size-5"></span>keyrove</a
>
><span data-icon="wordmark" class="h-5"></span
></a>
<span>Keydown benchmark</span>
</p>
<nav class="footer-links" aria-label="Footer">
Expand Down
2 changes: 1 addition & 1 deletion apps/bench/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 17 additions & 8 deletions apps/bench/src/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
Check,
Copy,
Download,
Keyboard,
Moon,
Play,
SlidersHorizontal,
Expand All @@ -13,6 +12,8 @@ import {
type IconNode,
} from 'lucide';

import { WORDMARK } from '../../docs/src/brand.ts';

/**
* The bench's icons, drawn as the docs site draws its own (see
* apps/docs/build/icons.ts): lucide at a 1.75 stroke, inlined into the
Expand All @@ -35,7 +36,6 @@ const ICONS = {
check: renderNode(Check),
copy: renderNode(Copy),
download: renderNode(Download),
keyboard: renderNode(Keyboard),
moon: renderNode(Moon),
play: renderNode(Play),
settings: renderNode(SlidersHorizontal),
Expand All @@ -44,17 +44,26 @@ const ICONS = {
warning: renderNode(TriangleAlert),
};

export type IconName = keyof typeof ICONS | 'github';
export type IconName = keyof typeof ICONS | 'github' | 'wordmark';

/** The GitHub mark, the one icon not from lucide — the docs header's own. */
const GITHUB =
'<svg class="CLASS shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 192" fill="none" stroke="currentColor" stroke-width="14" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M120.755 170c.03-4.669.059-20.874.059-27.29 0-9.272-3.167-15.339-6.719-18.41 22.051-2.464 45.201-10.863 45.201-49.067 0-10.855-3.824-19.735-10.175-26.683 1.017-2.516 4.413-12.63-.987-26.32 0 0-8.296-2.672-27.202 10.204-7.912-2.213-16.371-3.308-24.784-3.352-8.414.044-16.872 1.14-24.785 3.352C52.457 19.558 44.162 22.23 44.162 22.23c-5.4 13.69-2.004 23.804-.987 26.32C36.824 55.498 33 64.378 33 75.233c0 38.204 23.149 46.603 45.2 49.067-3.551 3.071-6.719 9.138-6.719 18.41 0 6.416.03 22.621.059 27.29M27 130c9.939.703 15.67 9.735 15.67 9.735 8.834 15.199 23.178 10.803 28.815 8.265" /></svg>';

/**
* The docs' wordmark, read from the docs rather than copied, as the stylesheet
* is. It names the link it sits in, so unlike the icons it is not hidden.
*/
const wordmark = (className: string) =>
`<svg class="${className} w-auto shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="${WORDMARK.viewBox}" role="img" aria-label="keyrove">${WORDMARK.body}</svg>`;

/** One inline `<svg>`; `className` carries its size. */
export const icon = (name: IconName, className: string) =>
name === 'github'
? GITHUB.replace('CLASS', className)
: `<svg class="${className} shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${ICONS[name]}</svg>`;
export const icon = (name: IconName, className: string) => {
if (name === 'github') return GITHUB.replace('CLASS', className);
if (name === 'wordmark') return wordmark(className);

return `<svg class="${className} shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${ICONS[name]}</svg>`;
};

/**
* The docs' placeholder: `<span data-icon="play" class="size-4"></span>`.
Expand All @@ -65,7 +74,7 @@ const PLACEHOLDER =

export const expandIcons = (html: string) =>
html.replace(PLACEHOLDER, (_match, name: string, className = '') => {
if (name !== 'github' && !(name in ICONS)) {
if (name !== 'github' && name !== 'wordmark' && !(name in ICONS)) {
throw new Error(`[bench] no icon named "${name}" in src/icons.ts.`);
}

Expand Down
40 changes: 15 additions & 25 deletions apps/docs/build/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,8 @@ import {
Underline,
} from 'lucide';

import {
CHROME_ICONS,
fromLucide,
LUCIDE_STROKE_WIDTH,
type IconDef,
} from '../src/icons.ts';
import { MARK } from '../src/brand.ts';
import { CHROME_ICONS, fromLucide, type IconDef } from '../src/icons.ts';

/**
* Icons, rendered into the page HTML at build time.
Expand Down Expand Up @@ -108,33 +104,27 @@ const ICONS = {
export type IconName = keyof typeof ICONS;

/**
* The keyboard mark on its own, as the body of `favicon.svg`.
* The logo's square mark (src/brand.ts), as the body of `favicon.svg`.
*
* The same glyph at the same weight as the header wordmark, with the two
* changes a standalone document forces: `currentColor` has nothing to inherit
* from out here, so the accent is written out; and there is no stylesheet, so
* the dark variant the wordmark gets from `dark:` is an inline media query.
* The header colours the wordmark from the page: its letters are
* `currentColor` and its ring follows `dark:`. A favicon has no page to
* inherit from, so it writes the colours out, and switches them with an
* inline media query.
*
* A file rather than a `data:` URI, which browsers accept but Google Search
* does not: it shows a favicon beside a result only when it can fetch one from
* a URL, and falls back to a generic globe otherwise.
*/
export const faviconSvg = (() => {
const { viewBox, body } = ICONS.keyboard;

const svg = [
'<svg xmlns="http://www.w3.org/2000/svg"',
`viewBox="${viewBox}"`,
'fill="none"',
// indigo-600 and, below, indigo-400 — the wordmark's two colours. The mark
// sits on browser chrome rather than on the page, so it follows the system
// theme whatever the header toggle has been set to.
'stroke="#4f46e5"',
`stroke-width="${LUCIDE_STROKE_WIDTH}"`,
'stroke-linecap="round"',
'stroke-linejoin="round">',
'<style>@media (prefers-color-scheme: dark) { svg { stroke: #818cf8 } }</style>',
body,
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${MARK.viewBox}">`,
// indigo-600 and zinc-900, then indigo-400 and zinc-100 — the header's
// colours. The mark sits on browser chrome rather than on the page, so it
// follows the system theme whatever the header toggle has been set to.
'<style>rect { stroke: #4f46e5 } path { fill: #18181b }',
'@media (prefers-color-scheme: dark) { rect { stroke: #818cf8 } path { fill: #f4f4f5 } }</style>',
MARK.ring,
MARK.letter,
'</svg>',
].join(' ');

Expand Down
42 changes: 21 additions & 21 deletions apps/docs/build/og-card.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,14 @@
color: #fafafa;
-webkit-font-smoothing: antialiased;
}
.wordmark {
display: flex;
align-items: center;
gap: 16px;
font-size: 34px;
font-weight: 600;
letter-spacing: -0.02em;
}
/* The logo from src/brand.ts: letters in the card's text colour, the ring
in indigo-400. */
.wordmark svg {
width: 44px;
height: 44px;
display: block;
height: 46px;
width: auto;
}
.wordmark-ring {
color: #818cf8;
}
h1 {
Expand Down Expand Up @@ -118,20 +115,23 @@
}
</style>
<div class="wordmark">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
>
<svg viewBox="0 0 4349 1120" role="img" aria-label="keyrove">
<rect
class="wordmark-ring"
x="45"
y="45"
width="789"
height="1030"
rx="210"
fill="none"
stroke="currentColor"
stroke-width="90"
/>
<path
d="M10 8h.01M12 12h.01M14 8h.01M16 12h.01M18 8h.01M6 8h.01M7 16h10M8 12h.01"
fill="currentColor"
d="M320 925L195 925L195 195L320 195L320 591L407 591L541 375L681 375L515 641L684 925L541 925L407 697L320 697L320 925ZM1220 935L1220 935Q1150 935 1098 908Q1046 881 1018 833Q989 784 989 719L989 719L989 581Q989 516 1018 468Q1046 419 1098 392Q1150 365 1220 365L1220 365Q1289 365 1341 392Q1392 419 1421 468Q1449 516 1449 581L1449 581L1449 680L1110 680L1110 719Q1110 777 1138 807Q1166 836 1221 836L1221 836Q1263 836 1289 822Q1315 807 1322 779L1322 779L1445 779Q1431 850 1370 893Q1308 935 1220 935ZM1110 602L1328 600L1328 580Q1328 523 1301 493Q1274 462 1220 462Q1166 462 1138 493Q1110 524 1110 581L1110 581L1110 602ZM1796 1105L1665 1105L1742 895L1537 375L1674 375L1776 656Q1784 679 1792 712Q1800 744 1804 765L1804 765Q1809 744 1817 712Q1825 679 1833 656L1833 656L1929 375L2061 375L1796 1105ZM2298 925L2173 925L2173 375L2291 375L2291 480L2291 480Q2301 437 2325 410L2325 410Q2365 365 2443 365L2443 365Q2532 365 2583 420Q2634 475 2634 572L2634 572L2634 612L2503 612L2503 581Q2503 528 2476 499Q2448 470 2400 470L2400 470Q2351 470 2325 499Q2298 528 2298 581L2298 581L2298 925ZM2959 934L2959 934Q2889 934 2838 908Q2786 881 2758 833Q2729 784 2729 718L2729 718L2729 582Q2729 516 2758 468Q2786 419 2838 393Q2889 366 2959 366L2959 366Q3030 366 3081 393Q3132 419 3161 468Q3189 516 3189 581L3189 581L3189 718Q3189 784 3161 833Q3132 881 3081 908Q3030 934 2959 934ZM2959 825L2959 825Q3009 825 3037 798Q3064 770 3064 718L3064 718L3064 582Q3064 529 3037 502Q3009 475 2959 475L2959 475Q2910 475 2882 502Q2854 529 2854 582L2854 582L2854 718Q2854 770 2882 798Q2910 825 2959 825ZM3620 925L3459 925L3278 375L3406 375L3511 709Q3520 739 3529 771Q3537 803 3541 822L3541 822Q3546 803 3554 771Q3562 739 3570 710L3570 710L3673 375L3800 375L3620 925ZM4120 935L4120 935Q4050 935 3998 908Q3946 881 3918 833Q3889 784 3889 719L3889 719L3889 581Q3889 516 3918 468Q3946 419 3998 392Q4050 365 4120 365L4120 365Q4189 365 4241 392Q4292 419 4321 468Q4349 516 4349 581L4349 581L4349 680L4010 680L4010 719Q4010 777 4038 807Q4066 836 4121 836L4121 836Q4163 836 4189 822Q4215 807 4222 779L4222 779L4345 779Q4331 850 4270 893Q4208 935 4120 935ZM4010 602L4228 600L4228 580Q4228 523 4201 493Q4174 462 4120 462Q4066 462 4038 493Q4010 524 4010 581L4010 581L4010 602Z"
/>
<rect width="20" height="16" x="2" y="4" rx="2" />
</svg>
keyrove
</div>
<div>
<h1>Keyboard navigation that <em>behaves itself</em>.</h1>
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mixedrays/keyrove-docs",
"version": "3.1.0",
"version": "3.1.1",
"private": true,
"description": "Documentation site and landing page for keyrove.",
"license": "MIT",
Expand Down
Binary file modified apps/docs/public/og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions apps/docs/src/brand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* The keyrove logo: the name with a focus ring round its first letter, the
* ring keyboard focus draws, sitting where focus starts.
*
* The letters are JetBrains Mono (SIL Open Font License 1.1), Bold in the
* wordmark and ExtraBold in the mark, outlined rather than set as text, so the
* logo looks the same on every system and the site loads no font for it. The
* ring takes the accent colour and the letters the text colour around them;
* the favicon, which has no page around it, writes both out.
*/

/**
* The wordmark, for the header and footer. Its ring carries the
* `wordmark-ring` class, which colours it; everything else is
* `currentColor`.
*/
export const WORDMARK = {
viewBox: '0 0 4349 1120',
body: [
'<rect class="wordmark-ring" x="45" y="45" width="789" height="1030" rx="210" fill="none" stroke="currentColor" stroke-width="90" />',
'<path fill="currentColor" d="M320 925L195 925L195 195L320 195L320 591L407 591L541 375L681 375L515 641L684 925L541 925L407 697L320 697L320 925ZM1220 935L1220 935Q1150 935 1098 908Q1046 881 1018 833Q989 784 989 719L989 719L989 581Q989 516 1018 468Q1046 419 1098 392Q1150 365 1220 365L1220 365Q1289 365 1341 392Q1392 419 1421 468Q1449 516 1449 581L1449 581L1449 680L1110 680L1110 719Q1110 777 1138 807Q1166 836 1221 836L1221 836Q1263 836 1289 822Q1315 807 1322 779L1322 779L1445 779Q1431 850 1370 893Q1308 935 1220 935ZM1110 602L1328 600L1328 580Q1328 523 1301 493Q1274 462 1220 462Q1166 462 1138 493Q1110 524 1110 581L1110 581L1110 602ZM1796 1105L1665 1105L1742 895L1537 375L1674 375L1776 656Q1784 679 1792 712Q1800 744 1804 765L1804 765Q1809 744 1817 712Q1825 679 1833 656L1833 656L1929 375L2061 375L1796 1105ZM2298 925L2173 925L2173 375L2291 375L2291 480L2291 480Q2301 437 2325 410L2325 410Q2365 365 2443 365L2443 365Q2532 365 2583 420Q2634 475 2634 572L2634 572L2634 612L2503 612L2503 581Q2503 528 2476 499Q2448 470 2400 470L2400 470Q2351 470 2325 499Q2298 528 2298 581L2298 581L2298 925ZM2959 934L2959 934Q2889 934 2838 908Q2786 881 2758 833Q2729 784 2729 718L2729 718L2729 582Q2729 516 2758 468Q2786 419 2838 393Q2889 366 2959 366L2959 366Q3030 366 3081 393Q3132 419 3161 468Q3189 516 3189 581L3189 581L3189 718Q3189 784 3161 833Q3132 881 3081 908Q3030 934 2959 934ZM2959 825L2959 825Q3009 825 3037 798Q3064 770 3064 718L3064 718L3064 582Q3064 529 3037 502Q3009 475 2959 475L2959 475Q2910 475 2882 502Q2854 529 2854 582L2854 582L2854 718Q2854 770 2882 798Q2910 825 2959 825ZM3620 925L3459 925L3278 375L3406 375L3511 709Q3520 739 3529 771Q3537 803 3541 822L3541 822Q3546 803 3554 771Q3562 739 3570 710L3570 710L3673 375L3800 375L3620 925ZM4120 935L4120 935Q4050 935 3998 908Q3946 881 3918 833Q3889 784 3889 719L3889 719L3889 581Q3889 516 3918 468Q3946 419 3998 392Q4050 365 4120 365L4120 365Q4189 365 4241 392Q4292 419 4321 468Q4349 516 4349 581L4349 581L4349 680L4010 680L4010 719Q4010 777 4038 807Q4066 836 4121 836L4121 836Q4163 836 4189 822Q4215 807 4222 779L4222 779L4345 779Q4331 850 4270 893Q4208 935 4120 935ZM4010 602L4228 600L4228 580Q4228 523 4201 493Q4174 462 4120 462Q4066 462 4038 493Q4010 524 4010 581L4010 581L4010 602Z" />',
].join(''),
};

/**
* The square mark: the k in its ring on a 24-unit grid, for the favicon.
* Larger and heavier in its ring than the wordmark's k, and the ring heavier
* too, because a favicon is drawn at 16px.
*/
export const MARK = {
viewBox: '0 0 24 24',
ring: '<rect x="2" y="2" width="20" height="20" rx="5.5" fill="none" stroke-width="2.4" />',
letter:
'<path d="M10.07 18.75L7.29 18.75L7.29 5.25L10.07 5.25L10.07 12.43L11.44 12.43L13.56 8.58L16.65 8.58L13.82 13.55L16.71 18.75L13.56 18.75L11.42 14.76L10.07 14.76L10.07 18.75Z" />',
};
7 changes: 3 additions & 4 deletions apps/docs/src/components/site-chrome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Icon } from '@/components/icon.tsx';
import { SearchTrigger } from '@/components/search.tsx';
import { SiteLink } from '@/components/site-link.tsx';
import { ThemeToggle } from '@/components/theme-toggle.tsx';
import { Wordmark } from '@/components/wordmark.tsx';

/**
* The header and footer every page shares.
Expand Down Expand Up @@ -34,8 +35,7 @@ export function SiteHeader({
<div className="flex items-center gap-2">
{menu}
<SiteLink href="/" className="wordmark">
<Icon name="keyboard" className="size-6" />
keyrove
<Wordmark className="h-6" />
</SiteLink>
</div>
<div className="flex items-center gap-1 sm:gap-4">
Expand Down Expand Up @@ -98,8 +98,7 @@ export function SiteFooter({ layout }: { layout: Layout }) {
>
<p className="footer-meta">
<SiteLink href="/" className="wordmark">
<Icon name="keyboard" className="size-5" />
keyrove
<Wordmark className="h-5" />
</SiteLink>
<span>
v{meta.packageVersion} &middot; docs v{meta.docsVersion} &middot;{' '}
Expand Down
18 changes: 18 additions & 0 deletions apps/docs/src/components/wordmark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { WORDMARK } from '@/brand.ts';

/**
* The keyrove wordmark. `className` carries the height; the width follows from
* the viewBox. It names the link it sits in, so it is an image, not decoration.
*/
export function Wordmark({ className }: { className?: string }) {
return (
<svg
className={className ? `${className} w-auto shrink-0` : 'w-auto shrink-0'}
xmlns="http://www.w3.org/2000/svg"
viewBox={WORDMARK.viewBox}
role="img"
aria-label="keyrove"
dangerouslySetInnerHTML={{ __html: WORDMARK.body }}
/>
);
}
2 changes: 0 additions & 2 deletions apps/docs/src/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
Braces,
Check,
Copy,
Keyboard,
LayoutGrid,
Menu,
Moon,
Expand Down Expand Up @@ -62,7 +61,6 @@ export const CHROME_ICONS = {
close: fromLucide(X),
menu: fromLucide(Menu),
book: fromLucide(BookOpen),
keyboard: fromLucide(Keyboard),
grid: fromLucide(LayoutGrid),
braces: fromLucide(Braces),
copy: fromLucide(Copy),
Expand Down
8 changes: 4 additions & 4 deletions apps/docs/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@
@apply mx-auto flex h-14 max-w-[90rem] items-center justify-between gap-4 px-6;
}

/* The mark is the keyboard glyph the nav used to spend on Examples. It takes
the accent colour so it reads as a logo rather than a fourth nav icon. */
/* The logo (src/brand.ts): its letters take the text colour, and the focus
ring round the k takes the accent. */
.wordmark {
@apply inline-flex items-center gap-2 text-base font-semibold tracking-tight;
@apply inline-flex items-center;
}

.wordmark svg {
.wordmark-ring {
@apply text-indigo-600 dark:text-indigo-400;
}

Expand Down
Loading