Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
77646b7
chore(webkit): adopt the design-system tooling
egermano Sep 10, 2026
deb8305
feat(docs): move the documentation home out of the content collection…
egermano Sep 10, 2026
dfb806f
refactor(webkit): type three components' props and stop overriding de…
egermano Sep 10, 2026
add0d35
fix(asides): stop the directive-label lookup throwing on an empty label
egermano Sep 10, 2026
2c48c19
fix(pricing): pass the current language to the community links
egermano Sep 10, 2026
324c2f7
refactor(scripts): use type-only imports where verbatimModuleSyntax r…
egermano Sep 10, 2026
b54a7cf
chore(types): clear the remaining astro check errors
egermano Sep 10, 2026
7559147
refactor(footer): rebuild the footer on the design system and move it…
egermano Sep 10, 2026
25bbbdc
refactor(header): swap the sign-in call to action for a Console button
egermano Sep 10, 2026
423cb25
refactor(page-header): let the masthead span the content column as a …
egermano Sep 10, 2026
41e2c86
style(on-this-page): apply the repo formatter
egermano Sep 10, 2026
30db362
Merge branch 'release/new-azion-docs' into feature/new-docs-template
egermano Sep 10, 2026
6acc026
chore(lint): clear the dead code and redundant escapes the new rules …
egermano Sep 10, 2026
3145726
fix(semrush): stop the link rewriter throwing on an undefined match c…
egermano Sep 10, 2026
cf57b00
refactor(webkit): bring the components up to the design-system lint r…
egermano Sep 10, 2026
133a806
refactor(docs-home): give the documentation home its own layout, with…
egermano Sep 10, 2026
0d12f13
chore: remove unsused dependencies
egermano Sep 10, 2026
6ee08f8
chore: update dependencies
egermano Sep 10, 2026
b33b609
refactor(docs-home): align the home with the design system reference
egermano Sep 10, 2026
1552a70
chore(deps): upgrade @aziontech/webkit to 5.0.0
egermano Sep 10, 2026
05e902f
refactor: compress comments to the webkit 5 authoring rules
egermano Sep 10, 2026
c2c3429
refactor(shell): match the footer and the docs shell to the design sy…
egermano Sep 10, 2026
3ffa2be
fix(footer): put the brand back in the social row and drop the signat…
egermano Sep 10, 2026
bdf1971
fix(sidebar): remove filter from sidebar
egermano Sep 10, 2026
98d2ecc
fix(pagination): give the previous/next pair the reference's markup
egermano Sep 10, 2026
be904dd
chore(deps): upgrade @aziontech/theme to 5.0.0
egermano Sep 10, 2026
1b5e1f6
fix(rail): use the X glyph the icon font actually ships
egermano Sep 11, 2026
6606bb0
feat(rail): close the outline with the design system's CTA
egermano Sep 11, 2026
3217071
fix(header): show the GitHub link at every width
egermano Sep 11, 2026
dd1f1cc
fix(search): render the search trigger after the command menu
egermano Sep 11, 2026
7141a9a
style(search): format HeaderSearchDialog with the repo's Prettier config
egermano Sep 11, 2026
ad26fee
fix(prose): reveal the heading anchor on hover instead of pinning it
egermano Sep 11, 2026
5f6c939
fix(prose): render MDX tables with the design system's table tokens
egermano Sep 11, 2026
3f26633
fix(layout): close the doc page with the prose contract's bottom step
egermano Sep 11, 2026
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: 3 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Lint with the webkit rules before every commit.
npx eslint .
npx stylelint "**/*.{css,scss,vue}"
13 changes: 3 additions & 10 deletions .stylelintrc.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
{
"extends": [
"@aziontech/webkit/stylelint-config"
],
"extends": ["@aziontech/webkit/stylelint-config"],
"overrides": [
{
"files": [
"**/*.vue",
"**/*.astro"
],
"files": ["**/*.vue", "**/*.astro"],
"customSyntax": "postcss-html"
},
{
"files": [
"**/*.scss"
],
"files": ["**/*.scss"],
"customSyntax": "postcss-scss"
}
]
Expand Down
70 changes: 70 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,73 @@

<!-- @aziontech/webkit -->
## @aziontech/webkit design system

This project uses the Azion design system. Its guidance is organized in four layers — know which
layer you're in, because only one of them is optional:

1. **Invariants (always-on, below)** — the non-negotiables. Follow them in every UI change.
2. **Enforcement (the machine — non-optional)** — ESLint blocks the mechanical invariants at lint/CI;
the `webkit` MCP grounds every component choice; the `webkit-ui-verifier` agent checks the rest at
runtime (axe, both themes, states). If lint or the verifier fails, the change is not done.
3. **Skills (on-demand guidance)** — invoke `/webkit-*` skills for how to do a task well.
4. **Reference (depth)** — `.claude/rules/webkit-*.md` carry the full standards behind the invariants.

### Invariants — always follow

- **Components first.** Reach for an `@aziontech/webkit` component before writing custom UI. Find one
via the `webkit` MCP (`suggest_component`) or `node_modules/@aziontech/webkit/catalog.json`.
- **Flat imports.** `import Button from '@aziontech/webkit/button'` (PascalCase binding = the subpath).
Never category-prefixed, `/src/`, deep-internal, or a bare-package barrel.
- **Tokens only.** Color, typography, shape, spacing come from `@aziontech/theme`
(`var(--primary)`, `var(--spacing-md)`, `text-button-lg`) — never hex, `rgb`, `hsl`, or Tailwind palette.
- **Never restyle a webkit component.** No `class`/`:class`/`style` on its tag — compose inside its
slots or use its props.
- **Styles + theme.** Load `src/webkit.css` once at the app entry — it `@import`s the theme's
Tailwind v4 stylesheet (tokens + `@import "tailwindcss"` + fonts) and `@source`s webkit so its
component classes compile. Import `webkit.css`, not `@aziontech/theme` bare (the `@source` is what
compiles webkit's classes). Light is the default; for dark set `<html data-theme="dark">`.
- **Accessible & keyboard-operable.** Label every field, keep a visible focus ring, mirror state in
ARIA, no click handler without a key handler.
- **Small bundles.** Prefer the `<name>-root` path (or specific sub-components), import icons
individually, lazy-load heavy overlays.
- **App-level setup at first use.** A few components declare a one-time wiring in their catalog
`setup` field (via the MCP's `get_component`). Toast: before the first `toast(...)` call, wire
`import { ToastPlugin } from '@aziontech/webkit/toast'` + `.use(ToastPlugin)` on `createApp()`
in the entry (the plugin mounts the region automatically); skip if already wired. `webkit doctor`
flags usage with missing setup.

### Enforced by lint (blocks — nothing here is a suggestion)

ESLint (`@aziontech/webkit/eslint-plugin` + `vuejs-accessibility`, wired by `webkit init`) fails the
build on: category-prefixed / deep-internal / barrel imports · hardcoded color · restyling a webkit
component · a custom element where a webkit component exists · a hand-rolled `modelValue`+`update`
instead of `defineModel` · a deprecated component · a whole-icon-set import · missing the
tree-shakeable root · and the static a11y set (alt-text, aria-props, aria-role, click-without-key,
label-has-for, no-autofocus). The rest — states, motion-reduce, contrast/behavioral a11y, both
themes, microcopy — is verified at runtime by `/webkit-ui-verify` (the `webkit-ui-verifier` agent
runs axe + screenshots), and taste is a review concern.

### Skills — invoke for how to build UI well

Start with `/webkit-ui-craft` (the umbrella that routes the rest). Get structure and states right
before any polish:

- **Structure:** `/webkit-ux-heuristics` (right component per moment) · `/webkit-ui-states` (state surface + async behavior) · `/webkit-form` · `/webkit-create-surface` (where a create lives: page vs drawer, Advanced, the commit bar) · `/webkit-errors` (where a failure goes: field, section Message, toast, auth card) · `/webkit-tables` (data-driven `<Table>` + cell recipes) · `/webkit-lists` (the index page around it + the filter system) · `/webkit-navigation`
- **Words:** `/webkit-microcopy` (Azion product copy: punctuation, sentence case, label vs message, one word per concept)
- **Foundation:** `/webkit-baseline-ui` (tokens, hierarchy, containers, responsive widths)
- **Cross-cutting quality:** `/webkit-theming-dark-mode` · `/webkit-data-viz`
- **Polish:** `/webkit-motion-polish` · `/webkit-impeccable-polish` (finish + earned delight)
- **Verify + migrate:** `/webkit-ui-verify` (runtime axe + both themes + states) · `/webkit-ds-adoption`

Mechanics of consuming the package (imports, tokens, tree-shaking) live in `/webkit-usage`.

### Agents — delegate a whole task

`webkit-expert` (which component + how) · `webkit-adopter` (the init/adoption flow) · `webkit-reviewer`
(review a diff for correct/performant usage) · `webkit-ui-verifier` (drive the screen: both themes,
widths, console, axe, states) · `webkit-adoption-auditor` (coverage scorecard: custom → webkit).

Full standards behind the invariants: `.claude/rules/webkit-*.md`.
<!-- @aziontech/webkit -->
## @aziontech/webkit design system

Expand Down
4 changes: 2 additions & 2 deletions astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import rehypeSlug from './plugins/rehype-slug-config'
import { rehypei18nAutolinkHeadings } from './plugins/rehype-i18n-autolink-headings';
import { rehypeOptimizeStatic } from './plugins/rehype-optimize-static';
import { rehypeTasklistEnhancer } from './plugins/rehype-tasklist-enhancer';
import rehypeScrollableTables from './plugins/rehype-scrollable-tables.js'
import rehypeDocTable from './plugins/rehype-doc-table'

type MarkdownConfig = NonNullable<AstroUserConfig['markdown']>;

Expand Down Expand Up @@ -58,7 +58,7 @@ export default defineConfig({
] as MarkdownConfig['remarkPlugins'],
rehypePlugins: [
rehypeSlug,
rehypeScrollableTables,
rehypeDocTable,
// This adds links to headings
// rehypeLinks, // disabling target Blank
[rehypeAutolinkHeadings, autoLinks],
Expand Down
12 changes: 10 additions & 2 deletions azion.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ export default {
variable: '${uri}',
conditional: 'if',
operator: 'matches',
// NOTE: this is a string, not a regex literal, so the original
// `\.` was already collapsing to a bare `.` (any character) before
// reaching the edge. Kept as-is to preserve routing; to match a
// literal dot the escape has to be doubled (`\\.`).
argument:
'\.(jpg|jpeg|png|gif|bmp|webp|svg|ico|ttf|otf|woff|woff2|eot|pdf|doc|docx|xls|xlsx|ppt|pptx|mp4|webm|mp3|wav|ogg|css|js|json|xml|html|txt|csv|zip|rar|7z|tar|gz|webmanifest|map|md|yaml|yml)$'
'.(jpg|jpeg|png|gif|bmp|webp|svg|ico|ttf|otf|woff|woff2|eot|pdf|doc|docx|xls|xlsx|ppt|pptx|mp4|webm|mp3|wav|ogg|css|js|json|xml|html|txt|csv|zip|rar|7z|tar|gz|webmanifest|map|md|yaml|yml)$'
}
]
],
Expand Down Expand Up @@ -112,7 +116,11 @@ export default {
variable: '${uri}',
conditional: 'if',
operator: 'matches',
argument: '^(?!.*\/$)(?![\s\S]*\.[a-zA-Z0-9]+$).*'
// NOTE: a string, not a regex literal — `\s\S` collapsed to `sS`
// and `\.` to `.` before the edge saw them, so the second lookahead
// has never excluded file-like URIs. Kept verbatim to preserve
// routing; doubling the escapes (`[\\s\\S]`, `\\.`) is the real fix.
argument: '^(?!.*/$)(?![sS]*.[a-zA-Z0-9]+$).*'
}
]
],
Expand Down
2 changes: 1 addition & 1 deletion backend/semrush-replace-url/helpers/csv.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ export async function read(filepath) {

return redirects;
} catch (error) {
throw new Error(`Error loading redirects: ${error.message}`);
throw new Error(`Error loading redirects: ${error.message}`, { cause: error });
}
}
6 changes: 3 additions & 3 deletions backend/semrush-replace-url/index.i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,20 @@ async function processFile(filePath, redirects) {
rgxDoubleQuote: rgxDoubleQuote.toString(),
url30x,
url200,
contentMatchCount: contentMatch.length,
contentMatchCount: (contentMatchSingleQuote?.length ?? 0) + (contentMatchDoubleQuote?.length ?? 0),
processedCount: counterFoundLinks
});

if(contentMatchSingleQuote.length) {
newContent = newContent.replace(
isRoot ? /'https\:\/\/www\.azion\.com\/'/ : rgxSingleQuote,
isRoot ? /'https:\/\/www\.azion\.com\/'/ : rgxSingleQuote,
`'${url200}'`
);
}

if(contentMatchDoubleQuote.length) {
newContent = newContent.replace(
isRoot ? /"https\:\/\/www\.azion\.com\/"/ : rgxDoubleQuote,
isRoot ? /"https:\/\/www\.azion\.com\/"/ : rgxDoubleQuote,
`'${url200}'`
);
}
Expand Down
6 changes: 3 additions & 3 deletions backend/semrush-replace-url/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { promises as fs } from 'fs';
import path from 'path'

import { read as readcsv } from './helpers/csv.js';
import { removeHostFromUrl, removeHostAndLangFromUrl, wwwazioncom, isFromRoot } from './helpers/url.js';
import { removeHostFromUrl, wwwazioncom, isFromRoot } from './helpers/url.js';

let counterFoundLinks = 0
let PATH = {
Expand Down Expand Up @@ -40,10 +40,10 @@ async function processFile(filePath, redirects) {
fileModified = true

if(contentMatchMdLinkAnchor.length)
newContent = findReplace(newContent, isRoot ? /\\(https\:\/\/www\.azion\.com\/\\)/ : rgxMdLink, `(${url200})`)
newContent = findReplace(newContent, isRoot ? /\\(https:\/\/www\.azion\.com\/\\)/ : rgxMdLink, `(${url200})`)

if(contentMatchMdLinkAnchor.length)
newContent = findReplace(newContent, isRoot ? /\\(https\:\/\/www\.azion\.com\/\\)/ : rgxMdLinkAnchor, `(${url200}#`)
newContent = findReplace(newContent, isRoot ? /\\(https:\/\/www\.azion\.com\/\\)/ : rgxMdLinkAnchor, `(${url200}#`)

console.log(`{
isRoot: ${isRoot},
Expand Down
16 changes: 0 additions & 16 deletions cicd/replace-links/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,6 @@ function readJsonFile(filePath) {
return json;
}

function jsonToFile(jsonObj, filePath) {
const jsonString = JSON.stringify(jsonObj, null, 2);

fs.writeFile(filePath, jsonString, 'utf8', (err) => {
if (err) {
console.error('An error occurred while writing JSON to file:', err);
} else {
console.log('JSON has been written successfully to', filePath);
}
});
};

async function touchJsonFileResult(data, fileDist) {
await jsonToFile(data, fileDist);
};

//////////////////
// end utils.js //
//////////////////
Expand Down
41 changes: 10 additions & 31 deletions integrations/astro-asides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,8 @@ export const asideAutoImport: Record<string, [string, string][]> = {
'~/components/Aside.astro': [['default', AsideTagname]],
};

/**
* remark plugin that converts blocks delimited with `:::` into instances of
* the `<Aside>` component. Depends on the `remark-directive` module for the
* core parsing logic.
*
* For example, this Markdown
*
* ```md
* :::tip[Did you know?]
* Astro helps you build faster websites with “Islands Architecture”.
* :::
* ```
*
* will produce this output
*
* ```astro
* <Aside type="tip" title="Did you know?">
* <p>Astro helps you build faster websites with “Islands Architecture”.</p>
* </Aside>
* ```
*/
/** remark plugin turning `:::tip[Did you know?]` blocks into `<Aside type title>`. */
// The `:::` parsing itself comes from `remark-directive`; this only rewrites its nodes.
function remarkAsides(): unified.Plugin<[], mdast.Root> {
const variants = new Set(['note', 'tip', 'caution', 'danger']);

Expand All @@ -42,21 +23,21 @@ function remarkAsides(): unified.Plugin<[], mdast.Root> {
const type = node.name;
if (!variants.has(type)) return;

// remark-directive converts a container’s “label” to a paragraph in
// its children, but we want to pass it as the title prop to <Aside>, so
// we iterate over the children, find a directive label, store it for the
// title prop, and remove the paragraph from children.
// remark-directive turns a container’s “label” into a child paragraph; lift it
// to the `title` prop and drop the paragraph.
let title: string | undefined;
remove(node, (child) => {
if (child.data?.directiveLabel) {
if ('children' in child && 'value' in child.children[0]) {
title = child.children[0].value;
// `in` alone leaves `children` as `unknown`, so narrow to the text node.
const children = 'children' in child ? child.children : undefined;
const first = Array.isArray(children) ? children[0] : undefined;
if (first && typeof first === 'object' && 'value' in first && typeof first.value === 'string') {
title = first.value;
}
return true;
}
});

// Replace this node with the aside component it represents.
parent.children[index] = makeComponentNode(
AsideTagname,
{ attributes: { type, title } },
Expand All @@ -70,9 +51,7 @@ function remarkAsides(): unified.Plugin<[], mdast.Root> {
};
}

/**
* Astro integration that sets up the remark plugin and auto-imports the `<Aside>` component everywhere.
*/
/** Registers the remark plugin and auto-imports `<Aside>` everywhere. */
export function astroAsides(): AstroIntegration {
return {
name: '@astrojs/asides',
Expand Down
8 changes: 1 addition & 7 deletions integrations/utils/makeComponentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ interface NodeProps {
attributes?: Record<string, string | boolean | number | undefined | null>;
}

/**
* Create AST node for a custom component injection.
*
* @example
* makeComponentNode('MyComponent', { prop: 'val' }, h('p', 'Paragraph inside component'))
*
*/
/** MDX AST node for a component injection: `makeComponentNode('Aside', { attributes }, ...children)`. */
export function makeComponentNode(
name: string,
{ attributes = {} }: NodeProps = {},
Expand Down
Loading
Loading