diff --git a/DESIGN.md b/DESIGN.md index 2cbc76f4..e474d91c 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -456,17 +456,18 @@ These are editorial sets, not necessarily database collections yet. If a set has - **The accent rail is banned**: a 2px rose border down a card's leading edge borrowed the shape of an error or callout border, and at a tall card's height it was the loudest thing on the page. `.lead-emphasis` spends the same accent as light instead of as a bar: a corner wash at 9% and a top hairline that fades out within a fifth of the width. Banned 2026-08-20. - **Install commands are syntax-coloured, always**: `npx`, `skilld`, `add`, and the flags stay quiet; the target and the skill name carry the accent, because that is the only part that differs between two commands. The accent rose is darkened to `oklch(0.44 0.17 17.32)` in light and lightened to `oklch(0.8 0.12 17.32)` in dark, since the button rose is 3.2:1 on `bg-muted` at small mono sizes. - **Weekly CTAs point at the email, never at a sign-in**: `See this week's` opens the public preview. A button labelled "get" that opens an OAuth wall is a promise the page does not keep, and Loop 1 admits no gate (VISION principle 3). -- **Trending rows lead with the Skill**: the Skill column outweighs the posts, which scroll beside it in a per-row carousel as testimony. The one acquisition CTA, an inert email preview beside `Sign in with GitHub`, sits in the page header on wide screens and after the fifth row on narrow ones. Confirmed 2026-08-21; the 9/3 sidebar gave way on 2026-10-01 because it took a quarter of the board's width. +- **Trending rows lead with the Skill**: the Skill column outweighs the posts, which wait behind the poster faces as testimony. The one acquisition CTA, an inert email preview beside `Sign in with GitHub`, sits in the page header on wide screens and after the fifth row on narrow ones. Confirmed 2026-08-21; the 9/3 sidebar gave way on 2026-10-01 because it took a quarter of the board's width. - **The dot is the atom**: stone dots are the noise, and one rose dot is the Skill you picked. The mark, the textures and the motifs all follow it, with one rose element per motif. Confirmed in the brand lab on 2026-10-06. - **The prompt caret replaces the rose chevron**: the mark is an ink caret plus one rose dot. In the lockup the dot trails the wordmark as a cursor. Harlan chose the trailing dot over a rose tittle on the `i`, so the dot reads as a cursor. Confirmed 2026-10-06. - **One geometry feeds every placement**: `shared/brand-mark.ts` holds the coordinates and the outlined wordmark. `AppLogo`, the OG lockup and every file in `public/` come from it, and `pnpm brand:assets` writes the files. The old chevron lived in hand-written copies, and their rose shades and wordmark weights had drifted apart. Confirmed 2026-10-06. - **Three textures replace the noise field**: Braille names for the hero, OG cards and the CLI; Converge for dividers and loading; File minimap for Skill pages and per-Skill OG cards. One job each, so no texture becomes wallpaper. Confirmed 2026-10-06. - **The lockup dot is an OG card's one rose element**: the 4px rose top bar, the rose quote rails and the rose avatar ring went stone. On the Splash card, the braille field radiates from the lockup's dot, so the texture spends no second rose. Confirmed 2026-10-06. - **OG cards repeat the homepage hero**: every card centres the lockup, a Plus Jakarta Sans title and its detail, with Braille names in the margins. Harlan chose this over a left-aligned editorial card and a card that leads with the run command. It supersedes the per-card textures above: the Skill card's file minimap, the Make a skill card's large `SKILL.md`, and the separate Splash and Make a skill templates are gone. Confirmed 2026-10-06. -- **The braille spark replaces the trending flame**: seven braille bars for seven days of mentions, today in rose. The emoji exception for Trending is retired. The spark keeps the sourced mention count beside it, which the flame never carried. Confirmed 2026-10-06. +- **The braille spark replaces the trending flame**: seven braille bars for seven days of mentions, today in rose. The emoji exception for Trending is retired. The spark keeps the sourced mention count beside it, which the flame never carried. On a trending row the poster faces carry that count, so the spark beside the name drops it. Confirmed 2026-10-06. - **Run and install read as different materials**: run is a dashed border with a hollow dot, because nothing stays; install is a solid border with a filled dot, because files land. Run is preselected. Confirmed 2026-10-06. - **Trending shares the /skills shell**: `/skills/trending` uses the full-width layout from `/skills`. Its heading replaces `CompactPageHeader` and lines up with the results column. A sticky 14rem sidebar holds the range links, then the weekly CTA and the skilld install. Nothing sits right of the board, so the board takes every column the sidebar leaves. Rows switch on their own width, and the post carousel shows two cards where it has room. On narrow screens the ranges are a compact row above the board, and the CTA stays after the fifth row. Harlan asked for this on 2026-10-06. It supersedes the 2026-10-01 call that dropped the 9/3 sidebar for taking a quarter of the board's width: that sidebar sat right of a 64rem board, and this one sits left of a board that runs the full page width. - **Trending rows scan in one pass**: the rank is a plain mono number, and the first three are a step darker. Each metadata line runs source, stars, then the signal that ranked the row, and an empty slot renders nothing. Descriptions stop at two lines, the run chip is as wide as its command, and the post carousel shows whole cards only. The sidebar invitation drops the email preview and the card. Its button is the rail's one solid rose element, so the install chip beside it inks its dot. Harlan asked for this polish on 2026-10-06. +- **Trending rows take two lines of words**: the first line says what the Skill does, and the second says why it ranked. A row ranked by posts shows at most three poster circles, the third counting the rest, then the first post as one quote line. The circles open the post carousel, and the quote line steps aside while the carousel shows. The braille spark sits beside the Skill name, where the shorter name line has room. Every post stays in the server HTML. A row dropped from 292px to 78px on desktop and from 377px to 163px on a phone. Harlan picked this from a lab of five takes on 2026-10-06. It supersedes the two-line descriptions and the carousel under every row. - **A three-step lifecycle band replaces the four tiles**: under the hero, one band maps the product in its real order: find Skills, run, fork or install, keep up to date. Each step is one link: a mono step name, then one short sans line. No markers, pitch lines or commands. A line of stone dots joins the steps. Run, the default, carries the band's one rose dot, so no step lights rose on hover. From 1024px, three centered columns sit under the centered hero; below that, a vertical list with the dots down the left. The hero and the band must fit a 1440×900 viewport. Harlan cut it from six steps on 2026-10-06, because six columns of mixed type read as too busy. - **A Developers menu groups the build surfaces**: the desktop header keeps Trending Skills and Find Skills as links, and folds CLI, MCP server, SDK and Make a skill into one Developers menu, as nuxtseo.com does. The trigger is a button, so a keyboard opens the panel without leaving the page; the panel's last link reaches `/developers`. Harlan asked for this on 2026-10-06. - **The Developers menu is a bento**: the three ways to connect stack as compact rows, each a lucide icon tile, a name and one line. Make a skill fills the second column as the one large card, with an ember atmosphere and the File minimap in its corner. It names both routes: the guides and Skillgen. `/make-skill` is the one entry for authors and routes to either, so Skillgen has no card of its own. The mobile menu lists the same four items as a plain group. Harlan chose this over five uniform cards on 2026-10-06. diff --git a/app/assets/css/main.css b/app/assets/css/main.css index 53bce69d..b40b36ee 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -951,47 +951,6 @@ text-decoration-color: currentColor; } -/* Trending cards: the repository link covers the card, and the source link - sits above it. */ -.home-trending-card { - position: relative; - display: flex; - height: 100%; - min-width: 0; - flex-direction: column; - border: 1px solid var(--ui-border); - border-radius: var(--ui-radius); - padding: 1rem; - transition: border-color 200ms ease-out; -} - -.home-trending-card__link::after { - content: ''; - position: absolute; - inset: 0; - border-radius: inherit; -} - -.home-trending-card__action { - position: relative; - z-index: 1; -} - -@media (hover: hover) { - .home-trending-card:has(.home-trending-card__link:hover) { - border-color: var(--ui-text-muted); - } -} - -.home-trending-card:has(.home-trending-card__link:focus-visible) { - outline: 2px solid var(--ui-primary); - outline-offset: 2px; -} - -.home-trending-card__link:focus-visible { - outline: none; -} - /* The homepage authoring link opens the setup wizard. */ .home-make-strip { display: grid; diff --git a/app/components/BrailleSpark.vue b/app/components/BrailleSpark.vue index 46f3e896..aecc3ec5 100644 --- a/app/components/BrailleSpark.vue +++ b/app/components/BrailleSpark.vue @@ -17,6 +17,7 @@ const { unit = SPARK_MENTIONS, period, showUnit = true, + showTotal = true, } = defineProps<{ /** Seven daily counts, oldest first, today last. */ counts: readonly number[] @@ -28,6 +29,12 @@ const { period?: string /** Show the unit after the count. Hide it only where a column heading names it. */ showUnit?: boolean + /** + * Show the count beside the bars. Hide it only where something beside the + * spark already counts, such as the poster faces on a trending row. The + * screen reader label keeps the count either way. + */ + showTotal?: boolean }>() const week = computed(() => sparkWeek(counts)) @@ -44,7 +51,7 @@ const label = computed(() => `${counted.value}. Per day, oldest first: ${week.va diff --git a/app/components/SkillCard.vue b/app/components/SkillCard.vue index 51f03031..09a8ac8b 100644 --- a/app/components/SkillCard.vue +++ b/app/components/SkillCard.vue @@ -30,6 +30,7 @@ const { actions, // Vue casts an absent boolean to false; an explicit undefined keeps the layout default. description = undefined, + descriptionLines = 2, rank, note, trending = false, @@ -43,6 +44,12 @@ const { actions?: readonly SkillCardAction[] /** Defaults to on, except in compact entries. */ description?: boolean + /** + * Cards and rows only: lines of description before the clamp. One where + * the entry spends its second line on something else, such as the posts + * that put a trending Skill on the board. + */ + descriptionLines?: 1 | 2 /** One-based position in a ranked list. */ rank?: number /** Why the Skill is here, in a curator's or editor's words. */ @@ -58,6 +65,8 @@ const emit = defineEmits<{ }>() const slots = defineSlots<{ + /** A small mark beside the name, such as a braille spark. */ + flag?: () => unknown /** Inline facts beside the metric, such as "Watching for changes" or a braille spark. */ meta?: () => unknown /** A block under the description, such as dependencies. */ @@ -87,8 +96,10 @@ const rankText = computed(() => view.value.rank == null ? null : String(view.val const rankLead = computed(() => (view.value.rank ?? 99) <= 3) const hasControls = computed(() => Boolean(view.value.run || view.value.like || view.value.sourceUrl || slots.actions)) const hasFacts = computed(() => Boolean(view.value.metric || slots.meta)) -/** A braille spark in the meta slot already spends the entry's rose. */ -const markAccent = computed(() => !slots.meta) +/** A braille spark in the meta or flag slot already spends the entry's rose. */ +const markAccent = computed(() => !slots.meta && !slots.flag) +/** The description clamp, unless a note above it takes the second line. */ +const descriptionClamp = computed(() => descriptionLines === 1 ? 'line-clamp-1' : 'line-clamp-2') /** * Rows in one list share their metric column, so its width comes from what @@ -102,13 +113,17 @@ const metricWidth = computed(() => view.value.metricKind === 'none' ? '0rem' : v
- + + +

{{ view.note }}

-

+

{{ view.description }}

@@ -151,13 +166,17 @@ const metricWidth = computed(() => view.value.metricKind === 'none' ? '0rem' : v
- + + +

{{ view.note }}

-

+

{{ view.description }}

@@ -434,6 +453,11 @@ const metricWidth = computed(() => view.value.metricKind === 'none' ? '0rem' : v margin-block-start: 0.875rem; } +/* An aside whose one block is hidden, such as posts behind a toggle, takes no space. */ +.skill-card__row-aside:has(> [hidden]:only-child) { + display: none; +} + .skill-card__row-end { grid-area: end; display: flex; diff --git a/app/components/skill-card/_SkillCardIdentity.vue b/app/components/skill-card/_SkillCardIdentity.vue index 2bdc4edb..2d4aa287 100644 --- a/app/components/skill-card/_SkillCardIdentity.vue +++ b/app/components/skill-card/_SkillCardIdentity.vue @@ -47,6 +47,7 @@ const where = computed(() => view.author ? view.repo : view.source) Trending +