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
8 changes: 5 additions & 3 deletions .specs/code-block.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
---


Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
Expand All @@ -234,7 +244,8 @@ const meta = {
defaultValue: 'js',
showLineNumbers: true,
copyAriaLabel: 'Copy code',
animateLines: false
animateLines: false,
border: true
}
}

Expand Down Expand Up @@ -357,3 +368,22 @@ export const WithAnimatedLines = {
}
}
}

/** @type {import('@storybook/vue3').StoryObj<typeof CodeBlock>} */
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"')
}
}
}
}
39 changes: 27 additions & 12 deletions apps/storybook/src/stories/foundations/Icons.stories.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -50,13 +51,29 @@ export const Overview = {
IconGrid
},
setup() {
const usageCode = [
'<i class="ai ai-azion"></i>',
'<i class="ai ai-edge-functions text-default text-2xl"></i>',
'<i class="pi pi-check"></i>',
'<i class="pi pi-times text-default text-2xl"></i>'
].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: [
'<i class="ai ai-azion"></i>',
'<i class="ai ai-edge-functions text-default text-2xl"></i>',
'<i class="pi pi-check"></i>',
'<i class="pi pi-times text-default text-2xl"></i>'
].join('\n')
}
]
return { icons, colorIcons, args, importTabs, usageTabs }
},
template: /* html */ `
<PageContainer>
Expand All @@ -67,13 +84,11 @@ export const Overview = {

<!-- Import -->
<SectionHeader title="Import" />
<CodeBlock label="JavaScript" class="mb-12">
import '@aziontech/icons';
</CodeBlock>
<CodeBlock :tabs="importTabs" :border="true" :show-line-numbers="false" class="h-fit mb-12" />

<!-- Usage -->
<SectionHeader title="Usage" />
<CodeBlock label="HTML" :content="usageCode" class="mb-6" />
<CodeBlock :tabs="usageTabs" :border="true" :show-line-numbers="false" class="h-fit mb-6" />
<p class="text-body-sm text-muted m-0 max-w-[620px] leading-relaxed mb-12">
Icons are a font, which means they inherit text properties.
<strong class="text-default">Colored</strong> brand icons (the <code>-cor</code> set) are
Expand Down
7 changes: 7 additions & 0 deletions packages/webkit/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down
10 changes: 7 additions & 3 deletions packages/webkit/src/components/data/code-block/code-block.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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<Props>(), {
Expand All @@ -75,7 +77,8 @@
defaultValue: undefined,
showLineNumbers: true,
copyAriaLabel: 'Copy code',
animateLines: false
animateLines: false,
border: true
})

const emit = defineEmits<{
Expand Down Expand Up @@ -446,10 +449,11 @@
<div
:class="
cn(
'flex w-full flex-col overflow-hidden rounded-[var(--shape-elements)] border border-[var(--border-default)] bg-[var(--bg-surface)]',
'flex w-full flex-col overflow-hidden rounded-[var(--shape-elements)] bg-[var(--bg-surface)] data-[border]:border data-[border]:border-[var(--border-default)]',
attrs.class as string | undefined
)
"
:data-border="border || null"
:data-testid="testId"
>
<div
Expand Down Expand Up @@ -523,7 +527,7 @@
</div>

<div
class="relative flex h-[320px] shrink-0 flex-col"
class="relative flex max-h-[320px] shrink-0 flex-col"
:data-testid="`${testId}__content`"
>
<div
Expand Down
Loading