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
3 changes: 2 additions & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ Motifs carry data. Each answers one pitch line from `COPY.md` and spends one ros

**Run chip.** It pairs with "No more skill bloat". Run and install sit side by side, and run is preselected. Run has a dashed border and a hollow rose dot, because nothing stays. Install has a solid border and a filled dot, because files land. A consequence line under the chip says what each choice leaves on disk.

**Braille spark.** It pairs with "Stay hyped". Seven braille bars (`β£€β£€β£Άβ£Ώ`) show seven days of social mentions, with today in rose. The mention count sits beside it. The spark is made of characters, so the same line works on the site, in the weekly email, in Discord and in a terminal. It replaces the trending flame emoji.
**Braille spark.** It pairs with "Stay hyped". Seven braille bars (`β£€β£€β£Άβ£Ώ`) show seven days of social mentions, with today in rose. The mention count sits beside it, with its noun from `sparkCount`, so one mention reads "1 mention". On the site, a day with no mentions draws the lowest bar at 40% opacity as a track, so a lone bar still reads as one day of seven. Plain text keeps the blank cell. The spark is made of characters, so the same line works on the site, in the weekly email, in Discord and in a terminal. It replaces the trending flame emoji.

**Change grid.** It pairs with "Keep updated". A 13-week grid of days, today at the bottom right. Each change is a stone dot, and the newest change is rose. A ring around a dot shows the size of the version bump, only where a version is known.

Expand Down Expand Up @@ -459,3 +459,4 @@ These are editorial sets, not necessarily database collections yet. If a set has
- **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.
- **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.
42 changes: 31 additions & 11 deletions app/components/BrailleSpark.vue
Original file line number Diff line number Diff line change
@@ -1,46 +1,58 @@
<script setup lang="ts">
import { brailleSpark, sparkWeek } from '#shared/braille-spark'
import type { SparkUnit } from '#shared/braille-spark'
import { SPARK_BARS, SPARK_MENTIONS, sparkCount, sparkLevels, sparkWeek } from '#shared/braille-spark'

/**
* Seven days of counts as seven braille bars, today in rose, with the count
* beside them. The bars are text from `shared/braille-spark.ts`, so the weekly
* email and any plain-text surface print the same spark.
*
* A day with no count draws the lowest bar faintly, as a track. Plain text
* leaves that day blank, and on the page a week with one post became one bar
* floating in a gap, with nothing to say it was a week.
*/
const {
counts,
total,
unit = 'mentions',
unit = SPARK_MENTIONS,
period,
showUnit = true,
} = defineProps<{
/** Seven daily counts, oldest first, today last. */
counts: readonly number[]
/** The count beside the bars. Defaults to the sum of the seven days. */
total?: number
/** What is counted, as a plural noun. The screen reader label always names it. */
unit?: string
/** What is counted, in both forms. The screen reader label always names it. */
unit?: SparkUnit
/** The window the count covers, such as `in 7 days`. It follows the noun. */
period?: string
/** Show the unit after the count. Hide it only where a column heading names it. */
showUnit?: boolean
}>()

const week = computed(() => sparkWeek(counts))
const bars = computed(() => [...brailleSpark(counts)])
const cells = computed(() => sparkLevels(counts).map(level => ({
glyph: SPARK_BARS[level === 0 ? 1 : level],
empty: level === 0,
})))
const shown = computed(() => total ?? week.value.reduce((sum, count) => sum + count, 0))
const totalText = computed(() => showUnit ? `${shown.value.toLocaleString('en-US')} ${unit}` : shown.value.toLocaleString('en-US'))
const label = computed(() => `${shown.value.toLocaleString('en-US')} ${unit}. Per day, oldest first: ${week.value.join(', ')}. Today: ${week.value.at(-1)}.`)
const counted = computed(() => [sparkCount(shown.value, unit), period].filter(Boolean).join(' '))
const totalText = computed(() => showUnit ? counted.value : shown.value.toLocaleString('en-US'))
const label = computed(() => `${counted.value}. Per day, oldest first: ${week.value.join(', ')}. Today: ${week.value.at(-1)}.`)
</script>

<template>
<span class="braille-spark" role="img" :aria-label="label">
<span class="braille-spark__bars" aria-hidden="true">{{ bars.slice(0, -1).join('') }}<span class="braille-spark__today">{{ bars.at(-1) }}</span></span>
<span class="braille-spark__bars" aria-hidden="true"><span v-for="(cell, index) in cells" :key="index" :class="cell.empty ? 'braille-spark__track' : index === cells.length - 1 && 'braille-spark__today'">{{ cell.glyph }}</span></span>
<span class="braille-spark__total" aria-hidden="true">{{ totalText }}</span>
</span>
</template>

<style scoped>
.braille-spark {
display: inline-flex;
align-items: center;
gap: 0.5rem;
align-items: baseline;
gap: 0.375rem;
font-family: var(--font-mono);
font-size: 0.75rem;
color: var(--ui-text-muted);
Expand All @@ -53,11 +65,19 @@ const label = computed(() => `${shown.value.toLocaleString('en-US')} ${unit}. Pe
font-family: 'IBM Plex Mono', 'DejaVu Sans Mono', 'Apple Braille', 'Segoe UI Symbol', ui-monospace, monospace;
font-size: 0.875rem;
line-height: 1;
letter-spacing: 0.04em;
/* Muted, not dimmed: the bars are a graphic and need 3:1 against the surface. */
color: var(--ui-text-muted);
}

/*
* The track is not data: it only shows where the week runs, and the label
* carries the count. A fraction of the bar colour, not the dimmed token: in
* dark mode dimmed sits within a step of muted, and a quiet day read as none.
*/
.braille-spark__track {
opacity: 0.4;
}

.braille-spark__today {
color: var(--brand-dot);
}
Expand Down
5 changes: 4 additions & 1 deletion app/components/SkilldInstallChip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import CommandChip from './_CommandChip.vue'
* installs the skilld-maintained skilld Skill globally, so the Agent can
* drive the CLI in every project.
*/
const { surface } = defineProps<{
const { surface, quiet = false } = defineProps<{
/** The analytics surface, such as `nav-promo`. */
surface: string
/** Ink the dot instead of rose, where a solid rose button shares the band. */
quiet?: boolean
}>()

const command = skilldSelfInstallCmd()
Expand All @@ -25,6 +27,7 @@ const consequenceId = useId()
:command="command"
mode="install"
size="sm"
:quiet="quiet"
:surface="surface"
:target="target"
:described-by="consequenceId"
Expand Down
12 changes: 12 additions & 0 deletions app/components/_CommandChip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const {
surface,
target,
size = 'md',
quiet = false,
copyLabel,
describedBy,
} = defineProps<{
Expand All @@ -27,6 +28,8 @@ const {
surface: string
target: InstallTarget
size?: 'md' | 'sm'
/** Ink the dot instead of rose, where another element already spends the band's rose. */
quiet?: boolean
copyLabel?: string
/** The id of the text that says what the command does. */
describedBy?: string
Expand Down Expand Up @@ -89,6 +92,7 @@ async function onCopy() {
:class="[
`command-chip--${mode}`,
`command-chip--${size}`,
quiet && 'command-chip--quiet',
marching && 'command-chip--march',
]"
>
Expand Down Expand Up @@ -215,6 +219,14 @@ async function onCopy() {
background-color: var(--brand-dot);
}

.command-chip--quiet .command-chip__dot {
border-color: var(--ui-text-muted);
}

.command-chip--quiet.command-chip--install .command-chip__dot {
background-color: var(--ui-text-muted);
}

.command-chip__command {
flex: 1 1 auto;
min-width: 0;
Expand Down
Loading