diff --git a/.specs/code-block.md b/.specs/code-block.md index 96f259062..146802f50 100644 --- a/.specs/code-block.md +++ b/.specs/code-block.md @@ -7,9 +7,9 @@ spec_version: 4 figma: url: https://www.figma.com/design/t97pXRs7xME3SJDs5iZ5RF/Webkit?node-id=4567-33761 node_id: 4567:33761 -checksum: 572ca2b4a32a976435e6cff29394d1da46c20942846803ff0474268343f69805 +checksum: 2a59538bb1ac5d21812069fe362232ea9af4e421cd04a1b7d9b8f3eeecd9246b created: 2026-05-28 -last_updated: 2026-07-01 +last_updated: 2026-07-13 --- @@ -68,6 +68,7 @@ For multiple languages, add one tab per snippet (each with its own `code`). Use | `showLineNumbers` | `boolean` | `true` | no | Shows a fixed-width gutter with zero-padded line numbers before each code line. | | `copyAriaLabel` | `string` | `'Copy code'` | no | Accessible name for the copy control (forwarded to CopyButton's aria-label). | | `animateLines` | `boolean` | `false` | no | Staggered line entrance for website layouts: each line slides from `-8px` with opacity `0 → 1`, `300ms` apart. | +| `border` | `boolean` | `true` | no | Draw the outer card border around the block. On by default; set `:border="false"` to render the block flush when it sits inside a surface that already frames it. Internal dividers (tab header, filename bar) are unaffected. | ## Type shapes @@ -120,7 +121,7 @@ For multiple languages, add one tab per snippet (each with its own `code`). Use When only one tab is provided and it has no `fileName`, the shell is code content + copy only (minimal block). -Code content scrolls inside `@aziontech/webkit/layout/scroll-area` (`orientation="both"`, 320 px height). Lines never wrap (`whitespace-pre`); on narrow viewports the scroll region grows horizontally so long lines stay on one row. The copy control (a `CopyButton`, `kind="outlined"` `size="small"`) is absolutely positioned on the non-scrolling content shell (`__content`) so it stays pinned top-right while lines scroll. +Code content scrolls inside `@aziontech/webkit/layout/scroll-area` (`orientation="both"`, capped at a `320 px` max height so shorter snippets fit their content and taller ones scroll). Lines never wrap (`whitespace-pre`); on narrow viewports the scroll region grows horizontally so long lines stay on one row. The copy control (a `CopyButton`, `kind="outlined"` `size="small"`) is absolutely positioned on the non-scrolling content shell (`__content`) so it stays pinned top-right while lines scroll. ## Motion & Animations @@ -185,6 +186,7 @@ Code content scrolls inside `@aziontech/webkit/layout/scroll-area` (`orientation - WithDiff — single tab with `lineChanges` for added/removed rows (Figma: Diff). - WithHighlightedLine — single tab with `highlightedLine={6}` (Figma: Highlighted). - WithAnimatedLines — single tab with `animateLines={true}` for staggered website entrance. +- Borderless — Default sample with `border={false}`; the outer card border is dropped so the block sits flush inside a surface that already frames it. ## Constraints — DO NOT diff --git a/apps/storybook/.storybook/test-visual/__image_snapshots__/linux/components-code-codeblock--borderless.png b/apps/storybook/.storybook/test-visual/__image_snapshots__/linux/components-code-codeblock--borderless.png new file mode 100644 index 000000000..17d445c52 Binary files /dev/null and b/apps/storybook/.storybook/test-visual/__image_snapshots__/linux/components-code-codeblock--borderless.png differ diff --git a/apps/storybook/.storybook/test-visual/__image_snapshots__/linux/foundations-icons--overview.png b/apps/storybook/.storybook/test-visual/__image_snapshots__/linux/foundations-icons--overview.png index 96e2071a7..9260d1fad 100644 Binary files a/apps/storybook/.storybook/test-visual/__image_snapshots__/linux/foundations-icons--overview.png and b/apps/storybook/.storybook/test-visual/__image_snapshots__/linux/foundations-icons--overview.png differ diff --git a/apps/storybook/src/stories/components/code/code-block/CodeBlock.stories.js b/apps/storybook/src/stories/components/code/code-block/CodeBlock.stories.js index 3b1a1ff32..e34a4bb82 100644 --- a/apps/storybook/src/stories/components/code/code-block/CodeBlock.stories.js +++ b/apps/storybook/src/stories/components/code/code-block/CodeBlock.stories.js @@ -218,6 +218,16 @@ const meta = { defaultValue: { summary: 'false' } } }, + border: { + control: 'boolean', + description: + 'Draw the outer card border around the block. On by default; set to false to render flush inside a surface that already frames it.', + table: { + category: 'props', + type: { summary: 'boolean' }, + defaultValue: { summary: 'true' } + } + }, 'onUpdate:value': { action: 'update:value', description: 'v-model:value update emitted when the active tab changes.', @@ -234,7 +244,8 @@ const meta = { defaultValue: 'js', showLineNumbers: true, copyAriaLabel: 'Copy code', - animateLines: false + animateLines: false, + border: true } } @@ -357,3 +368,22 @@ export const WithAnimatedLines = { } } } + +/** @type {import('@storybook/vue3').StoryObj} */ +export const Borderless = { + args: { + border: false + }, + render: Template, + parameters: { + docs: { + description: { + story: + 'The outer card border dropped via `:border="false"` — for embedding inside a surface that already frames the block. Internal dividers are unaffected.' + }, + source: { + code: snippet(languageSwitcherTabs, 'default-value="js" show-line-numbers :border="false"') + } + } + } +} diff --git a/apps/storybook/src/stories/foundations/Icons.stories.js b/apps/storybook/src/stories/foundations/Icons.stories.js index 4c82e16ab..122800f9a 100644 --- a/apps/storybook/src/stories/foundations/Icons.stories.js +++ b/apps/storybook/src/stories/foundations/Icons.stories.js @@ -1,9 +1,10 @@ import icons from '@aziontech/icons/catalog' import colorIcons from '@aziontech/icons/color-catalog' +import CodeBlock from '@aziontech/webkit/code-block' + import IconGrid from '../../foundations/components/IconGrid.vue' import { - CodeBlock, PageContainer, PageHeader, SectionHeader @@ -50,13 +51,29 @@ export const Overview = { IconGrid }, setup() { - const usageCode = [ - '', - '', - '', - '' - ].join('\n') - return { icons, colorIcons, args, usageCode } + const importTabs = [ + { + label: 'JavaScript', + value: 'js', + language: 'javascript', + code: "import '@aziontech/icons';" + } + ] + const usageTabs = [ + { + label: 'HTML', + value: 'html', + language: 'html', + fileName: 'index.html', + code: [ + '', + '', + '', + '' + ].join('\n') + } + ] + return { icons, colorIcons, args, importTabs, usageTabs } }, template: /* html */ ` @@ -67,13 +84,11 @@ export const Overview = { - - import '@aziontech/icons'; - + - +

Icons are a font, which means they inherit text properties. Colored brand icons (the -cor set) are diff --git a/packages/webkit/catalog.json b/packages/webkit/catalog.json index 3a92690c5..16f70efbc 100644 --- a/packages/webkit/catalog.json +++ b/packages/webkit/catalog.json @@ -2904,6 +2904,13 @@ "default": "false", "required": "no", "doc": "Staggered line entrance for website layouts: each line slides from `-8px` with opacity `0 → 1`, `300ms` apart." + }, + { + "name": "border", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Draw the outer card border around the block. On by default; set `:border=\"false\"` to render the block flush when it sits inside a surface that already frames it. Internal dividers (tab header, filename bar) are unaffected." } ], "events": [ diff --git a/packages/webkit/src/components/data/code-block/code-block.test.ts b/packages/webkit/src/components/data/code-block/code-block.test.ts index f12361af2..637c64833 100644 --- a/packages/webkit/src/components/data/code-block/code-block.test.ts +++ b/packages/webkit/src/components/data/code-block/code-block.test.ts @@ -342,6 +342,22 @@ describe('CodeBlock', () => { }) }) + describe('border', () => { + it('draws the outer border by default (data-border set)', () => { + const { getByTestId } = render(CodeBlock, { props: { tabs: singleTab } }) + + expect(getByTestId('data-code-block').getAttribute('data-border')).toBe('true') + }) + + it('drops the outer border when border is false (data-border absent)', () => { + const { getByTestId } = render(CodeBlock, { + props: { tabs: singleTab, border: false } + }) + + expect(getByTestId('data-code-block').getAttribute('data-border')).toBeNull() + }) + }) + describe('a11y (axe against styled DOM)', () => { it('single-tab layout has no violations', async () => { const { container } = render(CodeBlock, { diff --git a/packages/webkit/src/components/data/code-block/code-block.vue b/packages/webkit/src/components/data/code-block/code-block.vue index 779c181da..105febc35 100644 --- a/packages/webkit/src/components/data/code-block/code-block.vue +++ b/packages/webkit/src/components/data/code-block/code-block.vue @@ -67,6 +67,8 @@ copyAriaLabel?: string /** Staggered line entrance for website / marketing layouts (opacity + slide from -8 px). */ animateLines?: boolean + /** Draw the outer card border around the block. On by default; set to false to render flush inside a surface that already frames it. */ + border?: boolean } const props = withDefaults(defineProps(), { @@ -75,7 +77,8 @@ defaultValue: undefined, showLineNumbers: true, copyAriaLabel: 'Copy code', - animateLines: false + animateLines: false, + border: true }) const emit = defineEmits<{ @@ -446,10 +449,11 @@