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
5 changes: 5 additions & 0 deletions .changeset/release-list-prose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@codeatlasdev/ui": minor
---

`.ca-prose` styles release lists, so a changelog page needs no custom CSS (#126). Wrap one `<section class="ca-release">` per version (heading with its channel `.ca-chip`, then a `.ca-release__meta` row of date and links, then the rendered notes), add a `.ca-release-jump` pill index and close with `.ca-release-thanks`. Anchored releases clear a sticky `DocsShell` header (`scroll-margin-top`), and the meta links and pills keep 44px touch targets. Classes only, no JS or component: the markup can come from Markdown rendered at build time or at the edge.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ site/.astro/
# Generated brand assets (site/scripts/brand.ts, caBrandPlugin): hashed icons, social cards, URL module
site/public/brand/
site/src/generated/

# wrangler local state
.wrangler/
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ packages/ui/ → publishable library (@codeatlasdev/ui)
utils/ → debounce, clipboard, localStorage, palette-generator
index.ts → main barrel export
site/ → Astro site (landing + MDX docs + templates) at ui.codeatlas.com.br
functions/ → Cloudflare Pages Functions: /changelog/ (live GitHub Releases), /changelog.json, /changelog/feed.xml; tested with `bun run test:unit`
```

---
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ Publishing happens only in CI (`.github/workflows/release.yml`), triggered by a
2. When it's time to ship, a maintainer runs `bun run release` on an up-to-date, clean `main` (`--dry-run` first to see what goes out). It applies the pending changesets (version + CHANGELOG), commits `chore(release): @codeatlasdev/ui@X.Y.Z`, tags it, pushes `main` and the tag atomically and watches the run.
3. The tag starts the Release workflow: `scripts/release-meta.ts` checks the tag against the package version, `bun run validate` and the tarball smoke test run on the tagged commit (it does not depend on CI), then it publishes to npm with [provenance](https://docs.npmjs.com/generating-provenance-statements) through Trusted Publishing (OIDC, no npm token), creates the GitHub Release from the CHANGELOG and deploys the docs to ui.codeatlas.com.br.

The docs site shows releases without a redeploy: `/changelog/` is rendered at the edge by Cloudflare Pages Functions (`site/functions/`), which read the GitHub Releases of `codeatlasdev/ui` (cached about a minute, last good copy kept if GitHub is down). The page, `/changelog.json` and the Atom feed `/changelog/feed.xml` share that source. The build also renders `packages/ui/CHANGELOG.md` into the page as the fallback. Optional: set a `GITHUB_TOKEN` secret on the Pages project to lift the 60 requests/hour unauthenticated API limit; the cache keeps a single deployment far below it. Local check: `bun run build && cd site && bunx wrangler pages dev dist`.

Prereleases: `bunx changeset pre enter beta`, commit `.changeset/pre.json`, then `bun run release` → `X.Y.Z-beta.N` on the `beta` dist-tag (never `latest`), docs at beta.codeatlas-ui.pages.dev. `bunx changeset pre exit` before the next stable.

A failed or half-finished release is re-run for the same tag with `bun run release --publish @codeatlasdev/ui@X.Y.Z`; steps already done (npm version, GitHub Release) are skipped.
Expand Down
6 changes: 6 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/css-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Total: 791 unique classes across 80 components.

## Docs

`.ca-alert__content` `.ca-callout` `.ca-card-grid` `.ca-code` `.ca-codetab` `.ca-codetabs` `.ca-codetabs__bar` `.ca-codetabs__copy` `.ca-codetabs__panels` `.ca-codetabs__tab` `.ca-codetabs__tabs` `.ca-docs` `.ca-docs--bare` `.ca-docs-brand` `.ca-docs-content` `.ca-docs-header` `.ca-docs-header__inner` `.ca-docs-icon` `.ca-docs-main` `.ca-docs-menu` `.ca-docs-nav` `.ca-docs-nav__group` `.ca-docs-nav__label` `.ca-docs-nav__link` `.ca-docs-nav__title` `.ca-docs-search` `.ca-docs-search-btn` `.ca-docs-search-btn__label` `.ca-docs-search-icon` `.ca-docs-search-in` `.ca-docs-search-opt` `.ca-docs-search-out` `.ca-docs-search-pop-in` `.ca-docs-search-pop-out` `.ca-docs-search-results` `.ca-docs-search__bar` `.ca-docs-search__body` `.ca-docs-search__cancel` `.ca-docs-search__context` `.ca-docs-search__empty` `.ca-docs-search__enter` `.ca-docs-search__esc` `.ca-docs-search__icon` `.ca-docs-search__input` `.ca-docs-search__overlay` `.ca-docs-search__result` `.ca-docs-search__result--on` `.ca-docs-search__results` `.ca-docs-search__sr` `.ca-docs-search__text` `.ca-docs-search__title` `.ca-docs-section` `.ca-docs-sections` `.ca-docs-sheet-sections` `.ca-docs-sidebar` `.ca-docs-skip` `.ca-docs-spacer` `.ca-docs-theme` `.ca-docs-toc` `.ca-docs-toc-inline` `.ca-kbd` `.ca-link-card` `.ca-link-card__desc` `.ca-link-card__title` `.ca-method` `.ca-method--pill` `.ca-method--plain` `.ca-overlay` `.ca-prose` `.ca-rail-btn` `.ca-step` `.ca-steps` `.ca-toc` `.ca-toc--inline` `.ca-toc--rail` `.ca-toc__current` `.ca-toc__label` `.ca-toc__list` `.ca-toc__sub` `.ca-toc__summary-label`
`.ca-alert__content` `.ca-callout` `.ca-card-grid` `.ca-code` `.ca-codetab` `.ca-codetabs` `.ca-codetabs__bar` `.ca-codetabs__copy` `.ca-codetabs__panels` `.ca-codetabs__tab` `.ca-codetabs__tabs` `.ca-docs` `.ca-docs--bare` `.ca-docs-brand` `.ca-docs-content` `.ca-docs-header` `.ca-docs-header__inner` `.ca-docs-icon` `.ca-docs-main` `.ca-docs-menu` `.ca-docs-nav` `.ca-docs-nav__group` `.ca-docs-nav__label` `.ca-docs-nav__link` `.ca-docs-nav__title` `.ca-docs-search` `.ca-docs-search-btn` `.ca-docs-search-btn__label` `.ca-docs-search-icon` `.ca-docs-search-in` `.ca-docs-search-opt` `.ca-docs-search-out` `.ca-docs-search-pop-in` `.ca-docs-search-pop-out` `.ca-docs-search-results` `.ca-docs-search__bar` `.ca-docs-search__body` `.ca-docs-search__cancel` `.ca-docs-search__context` `.ca-docs-search__empty` `.ca-docs-search__enter` `.ca-docs-search__esc` `.ca-docs-search__icon` `.ca-docs-search__input` `.ca-docs-search__overlay` `.ca-docs-search__result` `.ca-docs-search__result--on` `.ca-docs-search__results` `.ca-docs-search__sr` `.ca-docs-search__text` `.ca-docs-search__title` `.ca-docs-section` `.ca-docs-sections` `.ca-docs-sheet-sections` `.ca-docs-sidebar` `.ca-docs-skip` `.ca-docs-spacer` `.ca-docs-theme` `.ca-docs-toc` `.ca-docs-toc-inline` `.ca-kbd` `.ca-link-card` `.ca-link-card__desc` `.ca-link-card__title` `.ca-method` `.ca-method--pill` `.ca-method--plain` `.ca-overlay` `.ca-prose` `.ca-rail-btn` `.ca-step` `.ca-steps` `.ca-toc` `.ca-toc--inline` `.ca-toc--rail` `.ca-toc__current` `.ca-toc__label` `.ca-toc__list` `.ca-toc__sub` `.ca-toc__summary-label` `.ca-release` `.ca-release-jump` `.ca-release-thanks` `.ca-release__meta`

## Drawer

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"validate": "bun scripts/validate.ts",
"postinstall": "node scripts/fix-ts-symlink.js && node scripts/install-hooks.js && bunx playwright install chromium",
"test": "bun run test:unit && bun run test:browser && bun run test:e2e",
"test:unit": "bun run --cwd packages/ui test:unit",
"test:unit": "bun run --cwd packages/ui test:unit && bun run --cwd site test:unit",
"test:browser": "bun run --cwd packages/ui test:browser",
"test:watch": "bun run --cwd packages/ui test:watch",
"test:e2e": "bun run --cwd site test:e2e",
Expand Down
47 changes: 47 additions & 0 deletions packages/ui/src/lib/components/Docs/Releases.svelte.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import { render } from "vitest-browser-svelte";
import { checkA11y } from "../../../../tests/a11y";
import Releases from "./Releases.test.svelte";

const css = (el: Element, prop: string) => getComputedStyle(el).getPropertyValue(prop);

describe("ca-release (changelog markup inside .ca-prose)", () => {
it("passes axe", async () => {
const screen = await render(Releases);
expect(await checkA11y(screen.container)).toHaveNoViolations();
});

it("separates releases with a rule and keeps anchored headings clear of a sticky header", async () => {
const screen = await render(Releases);
const section = screen.container.querySelector(".ca-release");
expect(section).toBeTruthy();
expect(css(section as Element, "border-top-width")).not.toBe("0px");
expect(css(section as Element, "scroll-margin-top")).not.toBe("0px");
});

it("lays the meta line out as a wrapping row of 44px-tall links", async () => {
const screen = await render(Releases);
const meta = screen.container.querySelector(".ca-release__meta") as Element;
expect(css(meta, "display")).toBe("flex");
expect(css(meta, "flex-wrap")).toBe("wrap");
for (const a of meta.querySelectorAll("a")) {
expect(a.getBoundingClientRect().height).toBeGreaterThanOrEqual(44);
}
});

it("the version jump list is a wrapping row of pills", async () => {
const screen = await render(Releases);
const list = screen.container.querySelector(".ca-release-jump ul") as Element;
expect(css(list, "display")).toBe("flex");
expect(css(list, "list-style-type")).toBe("none");
for (const a of screen.container.querySelectorAll(".ca-release-jump a")) {
expect(a.getBoundingClientRect().height).toBeGreaterThanOrEqual(44);
}
});

it("the thanks footer is set apart from the last release", async () => {
const screen = await render(Releases);
const thanks = screen.container.querySelector(".ca-release-thanks") as Element;
expect(css(thanks, "border-top-width")).not.toBe("0px");
});
});
29 changes: 29 additions & 0 deletions packages/ui/src/lib/components/Docs/Releases.test.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<script lang="ts">
// The markup `ca-release*` documents (and what a changelog page renders): inside `.ca-prose`.
</script>

<div class="ca-prose">
<nav class="ca-release-jump" aria-label="Versions">
<ul>
<li><a href="#v3-0-0">3.0.0</a></li>
<li><a href="#v2-4-0-beta-1">2.4.0-beta.1</a></li>
</ul>
</nav>
<section class="ca-release" id="v3-0-0" aria-labelledby="v3-0-0-h">
<h2 id="v3-0-0-h"><a href="#v3-0-0">3.0.0</a> <span class="ca-chip ca-chip--sm ca-chip--brand"><span class="ca-chip__label">latest</span></span></h2>
<p class="ca-release__meta">
<time datetime="2026-10-15">October 15, 2026</time>
<a href="https://www.npmjs.com/package/@codeatlasdev/ui/v/3.0.0">npm</a>
<a href="https://github.com/codeatlasdev/ui/releases">GitHub release</a>
</p>
<ul><li>A change</li></ul>
</section>
<section class="ca-release" id="v2-4-0-beta-1" aria-labelledby="v2-4-0-beta-1-h">
<h2 id="v2-4-0-beta-1-h"><a href="#v2-4-0-beta-1">2.4.0-beta.1</a></h2>
<ul><li>Another</li></ul>
</section>
<footer class="ca-release-thanks">
<h2 id="special-thanks">Special thanks</h2>
<p>To our supporters.</p>
</footer>
</div>
77 changes: 77 additions & 0 deletions packages/ui/src/lib/styles/prose.css
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,83 @@
/* Inside CodeTabs the tab already names the language. */
.ca-codetabs .ca-code[data-lang]::before { display: none; }

/* ── Release list (changelogs) ───────────────────────────────────────────── */
/* One `<section class="ca-release">` per version inside `.ca-prose`: heading (version + a `.ca-chip`
* for its channel), a `.ca-release__meta` row (date, links), then the rendered notes. `.ca-release-jump`
* is the version index (pills); `.ca-release-thanks` closes the page. */

.ca-prose .ca-release {
margin-top: 3rem;
padding-top: 1.5rem;
border-top: var(--ca-border-width) solid var(--ca-outline-subtle);
scroll-margin-top: calc(var(--ca-docs-header-h, 56px) + var(--ca-safe-top) + var(--ca-space-6));
}

.ca-prose .ca-release > h2 {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--ca-space-2);
margin: 0 0 var(--ca-space-1);
padding: 0;
}

.ca-prose .ca-release > h2 > a {
color: inherit;
text-decoration: none;
font-weight: inherit;
}

.ca-prose .ca-release > h2 > a:hover { text-decoration: underline; }

.ca-prose .ca-release__meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0 var(--ca-space-4);
margin: 0 0 var(--ca-space-2);
font-size: var(--ca-font-size-body2);
color: var(--ca-text-secondary);
}

.ca-prose .ca-release__meta a {
display: inline-flex;
align-items: center;
min-height: var(--ca-touch-target);
}

.ca-prose .ca-release-jump ul {
display: flex;
flex-wrap: wrap;
gap: var(--ca-space-1-5);
margin: 0;
padding: 0;
list-style: none;
}

.ca-prose .ca-release-jump li { margin: 0; padding: 0; }

.ca-prose .ca-release-jump a {
display: inline-flex;
align-items: center;
min-height: var(--ca-touch-target);
padding: 0 var(--ca-space-3);
border: var(--ca-border-width) solid var(--ca-outline-subtle);
border-radius: var(--ca-radius-pill);
font-size: var(--ca-font-size-body2);
text-decoration: none;
}

.ca-prose .ca-release-jump a:hover { border-color: var(--ca-outline-default); }

.ca-prose .ca-release-thanks {
margin-top: 3rem;
padding-top: 1.5rem;
border-top: var(--ca-border-width) solid var(--ca-outline-subtle);
}

.ca-prose .ca-release-thanks > h2 { margin-top: 0; }

@media (max-width: 899.98px) {
.ca-prose { line-height: 1.7; }
.ca-code pre,
Expand Down
Loading
Loading