Skip to content
Draft
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: 8 additions & 0 deletions src/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ declare module '*.md' {

export const metadata: Record<string, unknown>
}

declare module '*.svx' {
import type { SvelteComponent } from 'svelte'

export default class Comp extends SvelteComponent {}

export const metadata: Record<string, unknown>
}
57 changes: 57 additions & 0 deletions src/lib/components/Award.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
let { children, size } = $props()
</script>

<aside class="award">
<span class="size" aria-hidden="true">{size}</span>
<p class="content">
{@render children?.()}
</p>
</aside>

<style>
.award {
margin-block: var(--space-12) var(--space-20);
width: fit-content;
position: relative;
display: flow-root;

&::after {
content: '';
position: absolute;
z-index: -1;
inset: 1rem -1rem -1rem 1rem;
background-color: var(--bg-200);
}

&::before {
content: '';
position: absolute;
inset: 2rem -2rem -2rem 2rem;
z-index: -1;
border: 2px solid var(--bg-300);
}
}

.size {
font-size: var(--size-3xl);
font-weight: var(--font-ultrabold);
border: 4px solid var(--bg-500);
padding-block: var(--space-6);
padding-inline: var(--space-4);
float: left;
margin-inline-end: var(--space-8);
margin-block-end: var(--space-1);
background-color: var(--bg-300);
position: relative;
}

.content {
padding-block-start: var(--space-8);
padding-inline-start: var(--space-8);
font-style: italic;
margin: 0 !important; /* reset markdown margin */
font-size: var(--size-base);
text-wrap: balance;
}
</style>
186 changes: 186 additions & 0 deletions src/lib/components/BarChart.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<script lang="ts">
import { format_number } from '$lib/format-number'

type Props = {
data: Record<string, number>
formatter?: (value: number) => string
title: string
}

let { data, formatter = format_number, title }: Props = $props()

// Chart dimensions
const width = 928
const height = 384
const margin = { top: 30, right: 60, bottom: 40, left: 70 }
const chart_width = width - margin.left - margin.right
const chart_height = height - margin.top - margin.bottom
const bar_width = 39.5
const num_y_ticks = 10

// Calculate scales - derived from data
const max_value = $derived(Math.max(...Object.values(data)))
const y_max = $derived.by(() => {
// Determine rounding magnitude based on max value
if (max_value === 0) {
return 1
} else if (max_value < 1) {
return Math.ceil(max_value * 10) / 10 // Round to nearest 0.1
} else if (max_value <= 100) {
return Math.ceil(max_value / 10) * 10 // Round to nearest 10
}
return Math.ceil(max_value / 100) * 100 // Round to nearest 100
})
const y_scale = $derived(chart_height / y_max)
const bar_spacing = $derived(chart_width / Object.values(data).length)

// Generate y-axis ticks with nice round numbers
const y_ticks = $derived.by(() => {
// When all values are 0, only show a 0 tick
if (max_value === 0) {
return [{ value: 0, y: chart_height }]
}

// Calculate ideal step size
const ideal_step = y_max / (num_y_ticks - 1)

// Find magnitude and round to nice increment (1, 2, 5, 10, 20, 50, 100, etc.)
const magnitude = Math.pow(10, Math.floor(Math.log10(ideal_step)))
const normalized = ideal_step / magnitude
const nice_step = normalized <= 1 ? 1 : normalized <= 2 ? 2 : normalized <= 5 ? 5 : 10
const step = nice_step * magnitude

// Generate ticks from 0 to yMax using the nice step
const ticks = []
for (let value = 0; value <= y_max; value += step) {
const y = chart_height - value * y_scale
ticks.push({ value, y })
}
return ticks
})

// Generate bars
const bars = $derived(
Object.entries(data).map(([label, value], i) => {
const x = i * bar_spacing + bar_spacing / 2 - bar_width / 2
const bar_height = value * y_scale
const y = chart_height - bar_height
return {
x,
y,
width: bar_width,
height: bar_height,
label: label,
value: value,
center_x: i * bar_spacing + bar_spacing / 2
}
})
)

let uid = $props.id()
</script>

<div class="bar-chart">
<div id={uid} class="chart-title">{title}</div>

<svg xmlns="http://www.w3.org/2000/svg" class="chart" role="img" aria-labelledby={uid} viewBox="0 0 {width} {height}">
<g transform="translate({margin.left},{margin.top})">
<!-- Grid lines -->
<g class="grid" style="stroke: currentcolor; opacity: 0.2;" fill="none" text-anchor="end">
<path class="domain" stroke="currentColor" d="M{chart_width},{chart_height}H0V0H{chart_width}" />
{#each y_ticks as tick}
<g class="tick" opacity="1" transform="translate(0,{tick.y})">
<line stroke="currentColor" x2={chart_width} />
</g>
{/each}
</g>

<!-- Y-axis -->
<g fill="none" text-anchor="end">
<path stroke="currentColor" d="M-6,{chart_height}H0V0H-6" />
{#each y_ticks as tick}
<g class="tick" opacity="1" transform="translate(0,{tick.y})">
<line stroke="currentColor" x2="-6" />
<text fill="currentColor" x="-9" dy="0.32em" class="axis-label">
{formatter(tick.value)}
</text>
</g>
{/each}
</g>

<!-- X-axis -->
<g transform="translate(0,{chart_height})" fill="none" text-anchor="middle">
<path stroke="currentColor" d="M0,6V0H{chart_width}V6" />
{#each bars as bar}
<g class="tick" opacity="1" transform="translate({bar.center_x},0)">
<line stroke="currentColor" y2="6" />
<text fill="currentColor" y="9" dy="0.7em" text-anchor="middle" class="axis-label">
{bar.label}
</text>
</g>
{/each}
</g>

<!-- Bars -->
{#each bars as bar}
<rect x={bar.x} y={bar.y} width={bar.width} height={bar.height} fill="currentColor" class="bar" />
<text x={bar.center_x} y={bar.y - 10} text-anchor="middle" fill="currentColor" class="bar-label">
{formatter(bar.value)}
</text>
{/each}
</g>
</svg>

<details>
<summary>View chart as table</summary>
<table>
<caption>{title}</caption>
<thead>
<tr>
<th scope="col">Percentile</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
{#each bars as bar}
<tr>
<th scope="row">{bar.label}</th>
<td>{formatter(bar.value)}</td>
</tr>
{/each}
</tbody>
</table>
</details>
</div>

<style>
.chart-title {
text-transform: uppercase;
font-size: var(--size-sm);
font-weight: var(--font-bold);
text-align: center;
}

.axis-label {
font-size: 1rem;
}

.bar {
fill: var(--accent-400);
}

.bar-label {
font-size: 1rem;
fill: var(--fg-200);
}

text {
fill: currentColor;
color: var(--fg-200);
}

summary {
text-align: center;
font-size: var(--size-base);
}
</style>
14 changes: 14 additions & 0 deletions src/lib/components/Formula.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
let { children } = $props()
</script>

<div class="formula">
{@render children?.()}
</div>

<style>
.formula {
font-size: var(--size-2xl);
font-family: cursive;
}
</style>
10 changes: 8 additions & 2 deletions src/lib/components/Heading.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import type { SvelteHTMLElements } from 'svelte/elements'
type HeadingElement = 'h1' | 'h2' | 'h3' | 'h4' | 'span' | 'div'
type HeadingSize = undefined | 1 | 2 | 3 | 4 | 5 | 6
type HeadingSize = undefined | 1 | 2 | 3 | 4 | 5 | 6 | 0

const SIZE_MAP = {
['h1' as HeadingElement]: 1,
Expand All @@ -19,7 +19,7 @@

let { element, size, children, class: className, ...rest }: SvelteHTMLElements['div'] & Props = $props()

let calculated_size = $derived(size || SIZE_MAP[element])
let calculated_size = $derived(size ?? SIZE_MAP[element])
</script>

<svelte:element this={element} class={[`heading heading-size-${calculated_size}`, className]} {...rest}>
Expand All @@ -36,6 +36,12 @@
text-wrap: pretty;
}

.heading-size-0 {
color: light-dark(var(--black), var(--white));
font-weight: var(--font-ultrabold);
font-size: var(--size-7xl);
}

h1,
.heading-size-1 {
color: light-dark(var(--black), var(--white));
Expand Down
27 changes: 23 additions & 4 deletions src/lib/components/Markdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@
max-width: 60ch;
}

:global(.markdown svg[role='img']) {
max-width: 80ch;
}

:global(.markdown math) {
font-size: var(--size-3xl);
display: block;
text-align: center;
color: var(--fg-100);
overflow-x: auto;
}

:global(.markdown strong) {
font-style: normal;
font-weight: var(--font-bold);
Expand All @@ -68,8 +80,12 @@
font-style: italic;
}

:global(.markdown :is(p, pre, img, table, ol, ul, blockquote, figure, iframe)) {
margin-top: var(--space-6);
:global(.markdown :is(p, pre, img, table, ol, ul, blockquote, figure, iframe, math, .bar-chart)) {
margin-block-start: var(--space-6);

@media (height > 44rem) {
margin-block-start: var(--space-12);
}
}

:global(.markdown pre) {
Expand Down Expand Up @@ -189,11 +205,14 @@
}

:global(.markdown aside) {
border: 2px solid var(--accent);
padding: var(--space-6) var(--space-5);
margin: var(--space-16) 0;
}

:global(.markdown aside):not([class]) {
border: 2px solid var(--accent-800);
padding: var(--space-6) var(--space-5);
}

:global(.markdown :is(aside, blockquote) > :first-child) {
margin-top: 0;
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/Nav.items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export const items = [
{ url: '/design-tokens', title: 'Design Tokens' },
{ url: '/get-css', title: 'CSS Scraper' },
{ url: '/css-layers-visualizer', title: 'Visualize @layer' },
{ url: '/custom-property-inspector', title: 'Custom properties' }
{ url: '/custom-property-inspector', title: 'Custom properties' },
{ url: '/the-css-selection/2026', title: 'The CSS Selection 2026 ' }
]
3 changes: 2 additions & 1 deletion src/lib/components/Table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
border-inline-start: 1px solid var(--fg-450);
}

th {
th,
td:first-child {
white-space: nowrap;
}

Expand Down
Loading
Loading