Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
388d8de
Update styling in List and Tables for platform-architecture-control-…
webstackdev Mar 18, 2026
2c9f685
Update styling in List and Tables for platform-engineering-metrics-l…
webstackdev Mar 18, 2026
ad839b4
Update styling in List and Tables for postgresql-connection-pooling-…
webstackdev Mar 18, 2026
a4429d4
Update styling in List and Tables for private-networking-dns-routing…
webstackdev Mar 18, 2026
b1cc27a
Update styling in List and Tables for prometheus-high-cardinality-me…
webstackdev Mar 18, 2026
9cdd24d
Update styling in List and Tables for rate-limiting-token-bucket-lea…
webstackdev Mar 18, 2026
371348f
Update styling in List and Tables for release-quality-gates-automate…
webstackdev Mar 18, 2026
af1fb8e
Update styling in List and Tables for reverse-engineering-documentat…
webstackdev Mar 18, 2026
f0ad9db
Update styling in List and Tables for service-catalog-metadata-schem…
webstackdev Mar 18, 2026
112dcf8
Update styling in List and Tables for service-decommissioning-scream…
webstackdev Mar 19, 2026
3d1c5e1
Update styling in List and Tables for slo-error-budget-practical-gui…
webstackdev Mar 19, 2026
db7c6f9
Merge remote-tracking branch 'origin' into feature/next-batch-article…
webstackdev Mar 19, 2026
2a265d3
Styling fixes to table and list components in slsa-build-provenance-a…
webstackdev Mar 19, 2026
fda0522
Styling fixes to table and list components in strangler-fig-migration…
webstackdev Mar 19, 2026
efd9222
Styling fixes to table and list components in structured-logging-corr…
webstackdev Mar 19, 2026
2ff446e
Styling fixes to table and list components in symptom-based-alerting-…
webstackdev Mar 19, 2026
bf4eea5
Styling fixes to table and list components in synthetic-test-data-pii…
webstackdev Mar 19, 2026
3ad3c2c
Styling fixes to table and list components in terraform-module-design…
webstackdev Mar 19, 2026
0e8dcfb
Styling fixes to table and list components in terraform-state-locking…
webstackdev Mar 20, 2026
f3a3eac
Styling fixes to table and list components in workload-identity-feder…
webstackdev Mar 20, 2026
3cc5015
Fix lint errors
webstackdev Mar 20, 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
6 changes: 3 additions & 3 deletions _TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,6 @@ https://mermaid.js.org/config/directives.html

- Improve `<abbr>` styling: https://codepen.io/ire/pen/NoqWpm

- Moving the scroll bar up quickly with the mouse seems to make the header logic break - the Switcher component and Breadcrumbs are hidden under the header

## List Component

- Task list checked variant Markdown in dark theme is awkward, it has a dark shadow
Expand All @@ -240,6 +238,8 @@ https://mermaid.js.org/config/directives.html

- Need to improve the "squish" animation where the header reduces in size on scroll down, and returns to full size on scroll up. Maybe reduce and expand the text and search / themepicker / hamburger menu sizes in place, and then slide them horizontally.

- Moving the scroll bar up quickly with the mouse seems to make the header logic break - the Switcher component and Breadcrumbs are hidden under the header

## Content Issues

- Need an article on OpenStack
Expand All @@ -252,7 +252,7 @@ https://mermaid.js.org/config/directives.html

- Add a "Preview Special" item to our Download CTA that lets the user know the Deep Dive content can be previewed in HTML format, and offer a switch to it.

- How can we handle footnotes in List components? src/content/articles/kubernetes-multi-cluster-fleet-management-configuration/pdf.mdx line 80
- Tags should break more evenly across two lines when there's a lot of them, instead of forcing the author name and date to break across two lines: platform-engineering-metrics-lead-time-developer-friction/index.mdx

- Need to update Case Studies with lists and tables too

Expand Down
2,503 changes: 694 additions & 1,809 deletions package-lock.json

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@
"dependencies": {
"@adobe/remark-gridtables": "^3.0.18",
"@astrojs/check": "0.9.8",
"@astrojs/db": "^0.20.0",
"@astrojs/mdx": "5.0.1",
"@astrojs/preact": "5.0.1",
"@astrojs/db": "^0.20.1",
"@astrojs/mdx": "5.0.2",
"@astrojs/preact": "5.0.2",
"@astrojs/rss": "4.0.17",
"@astrojs/sitemap": "^3.7.1",
"@astrojs/vercel": "^10.0.1",
"@astrojs/vercel": "^10.0.2",
"@axe-core/playwright": "^4.11.1",
"@eslint-community/eslint-plugin-eslint-comments": "^4.7.1",
"@eslint/js": "10.0.1",
Expand All @@ -87,12 +87,12 @@
"@playwright/browser-chromium": "^1.58.2",
"@playwright/test": "1.58.2",
"@semantic-ui/astro-lit": "^5.3.0",
"@sentry/astro": "^10.43.0",
"@sentry/browser": "^10.43.0",
"@sentry/astro": "^10.45.0",
"@sentry/browser": "^10.45.0",
"@shikijs/transformers": "^4.0.2",
"@tailwindcss/forms": "0.5.11",
"@tailwindcss/typography": "0.5.19",
"@tailwindcss/vite": "^4.2.1",
"@tailwindcss/vite": "^4.2.2",
"@testing-library/dom": "10.4.1",
"@testing-library/preact": "3.2.4",
"@testing-library/user-event": "14.6.1",
Expand All @@ -105,7 +105,7 @@
"@types/glidejs__glide": "^3.6.6",
"@types/hast": "^3.0.4",
"@types/js-cookie": "^3.0.6",
"@types/jsdom": "^28.0.0",
"@types/jsdom": "^28.0.1",
"@types/node": "^25.5.0",
"@types/nodemailer": "^7.0.11",
"@types/pubsub-js": "^1.8.6",
Expand All @@ -129,11 +129,11 @@
"@vitest/coverage-v8": "^4.1.0",
"@webcomponents/template-shadowroot": "^0.2.1",
"alex": "^11.0.1",
"astro": "6.0.5",
"astro": "6.0.7",
"astro-link-validator": "github:rodgtr1/astro-link-validator",
"astro-og-canvas": "^0.10.1",
"astro-vtbot": "^2.1.12",
"baseline-browser-mapping": "^2.10.8",
"baseline-browser-mapping": "^2.10.9",
"canvas-confetti": "^1.9.4",
"confusing-browser-globals": "1.0.11",
"cross-env": "^10.1.0",
Expand Down Expand Up @@ -171,7 +171,7 @@
"md-attr-parser": "^1.3.0",
"mermaid": "^11.13.0",
"nanostores": "^1.2.0",
"nodemailer": "^8.0.2",
"nodemailer": "^8.0.3",
"npm": "^11.11.1",
"playwright-lighthouse": "^4.0.0",
"postcss": "8.5.8",
Expand Down Expand Up @@ -207,16 +207,16 @@
"retext": "^9.0.0",
"retext-smartypants": "^6.2.0",
"rimraf": "6.1.3",
"sanitize-html": "^2.17.1",
"sanitize-html": "^2.17.2",
"schema-dts": "^1.1.5",
"sharp": "^0.34.5",
"shiki": "^4.0.2",
"space-separated-tokens": "^2.0.2",
"stylelint": "^17.4.0",
"stylelint": "^17.5.0",
"stylelint-config-standard": "^40.0.0",
"stylelint-declaration-block-no-ignored-properties": "3.0.0",
"stylelint-order": "8.1.1",
"tailwindcss": "^4.2.1",
"tailwindcss": "^4.2.2",
"temp-dir": "3.0.0",
"timezones-ical-library": "^2.1.3",
"title-case": "4.3.2",
Expand All @@ -230,8 +230,8 @@
"unist-util-is": "^6.0.1",
"unist-util-visit": "^5.1.0",
"uuid": "^13.0.0",
"vercel": "^50.32.5",
"vite": "^8.0.0",
"vercel": "^50.34.2",
"vite": "^8.0.1",
"vitest": "4.1.0",
"vitest-axe": "0.1.0",
"workbox-build": "7.4.0",
Expand Down
32 changes: 32 additions & 0 deletions src/components/Icon/icons/dollar.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
export type Props = {
color: string
classes?: string
size: number
accessible: boolean
focusable: boolean
isListMarker?: boolean
id?: string
}

const { color, classes, size, accessible, focusable, isListMarker, id } = Astro.props
---

<svg
id={id}
xmlns="http://www.w3.org/2000/svg"
class:list={["shrink-0", `w-${size} h-${size}`, `text-${color}`, classes, isListMarker ? "mt-0.5" : ""]}
fill="currentColor"
stroke="none"
viewBox="0 0 24 24"
aria-hidden={accessible ? "false" : "true"}
focusable={focusable ? "true" : "false"}
>
{accessible && <title>Dollar Icon</title>}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M 13.091417,10.854545 V 4.6363636 A 8.4,8.4 0 0 1 15.927781,5.7818182 1.2,1.2 0 0 0 17.61869,5.4 0.98181818,0.98181818 0 0 0 17.400508,3.8181818 9.3272727,9.3272727 0 0 0 13.091417,2.2909091 v -1.2 a 1.0909091,1.0909091 0 0 0 -2.181818,0 v 1.0909091 c -3.4363628,0.2727273 -5.8909088,2.4 -5.8909088,5.2909091 v 0 c 0,3.2727277 2.345455,4.5818177 5.8909088,5.4545457 v 6.490909 a 9.1090909,9.1090909 0 0 1 -3.9818178,-1.690909 1.0909091,1.0909091 0 0 0 -1.636364,0.218182 c -0.545454,0.70909 -0.490909,1.254545 0.05455,1.636363 a 10.8,10.8 0 0 0 5.5636358,2.127273 v 1.2 a 1.0909091,1.0909091 0 0 0 2.181818,0 v -1.145455 c 3.545455,-0.272727 6,-2.4 6,-5.454545 v 0 c 0,-2.836364 -1.8,-4.472727 -6,-5.454546 z M 7.6368722,7.3636364 v 0 c 0,-1.5272728 1.309091,-2.6181819 3.2727268,-2.7818182 V 10.363636 C 8.1823262,9.6 7.6368722,8.672727 7.6368722,7.3636364 Z m 8.8363638,9.1636366 c 0,1.581818 -1.363637,2.727272 -3.381819,2.945454 v -6 c 2.781819,0.763637 3.381819,1.690909 3.381819,3 z"
></path>
</svg>
9 changes: 9 additions & 0 deletions src/components/List/ListItem.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
export type Props = {
lead?: string
}

const { lead } = Astro.props as Props
---

<wsb-list-item {...(lead ? { 'data-lead': lead } : {})}><slot /></wsb-list-item>
16 changes: 16 additions & 0 deletions src/components/List/__fixtures__/mixedApi.fixture.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
import List from '@components/List/index.astro'
import ListItem from '@components/List/ListItem.astro'
---

<List
variant="chevron-list"
items={[
{
lead: 'Prop item',
text: 'This should fail because slot items are also provided.',
},
]}
>
<ListItem lead="Slot item">This should never render.</ListItem>
</List>
13 changes: 13 additions & 0 deletions src/components/List/__fixtures__/richItems.fixture.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
import List from '@components/List/index.astro'
import ListItem from '@components/List/ListItem.astro'
---

<List variant="chevron-list">
<ListItem lead="Network isolation">
Separate VPCs<sup data-footnote-ref="slot-demo">1</sup> can provide stronger boundaries.
</ListItem>
<ListItem lead="Compute isolation">
Dedicated node pools reduce resource contention.
</ListItem>
</List>
23 changes: 23 additions & 0 deletions src/components/List/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { beforeEach, describe, expect, test } from 'vitest'
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
import { withJsdomEnvironment } from '@test/unit/helpers/litRuntime'
import MixedApiFixture from '@components/List/__fixtures__/mixedApi.fixture.astro'
import RichItemsFixture from '@components/List/__fixtures__/richItems.fixture.astro'

describe('List (Astro)', () => {
let container: AstroContainer
Expand Down Expand Up @@ -232,4 +234,25 @@ describe('List (Astro)', () => {
expect(title?.textContent).toContain('Resource metrics')
})
})

test('renders rich ListItem children through the existing layout item API', async () => {
const renderedHtml = await container.renderToString(RichItemsFixture)

await withJsdomEnvironment(async ({ window }) => {
window.document.body.innerHTML = renderedHtml

const items = Array.from(window.document.querySelectorAll('ul > li'))
expect(items).toHaveLength(2)
expect(items[0]?.querySelector('em')?.textContent).toContain('Network isolation')
expect(items[0]?.querySelector('sup[data-footnote-ref="slot-demo"]')?.textContent).toBe('1')
expect(items[1]?.textContent).toContain('Dedicated node pools reduce resource contention.')
expect(window.document.querySelector('wsb-list-item')).toBeNull()
})
})

test('throws a BuildError when items and ListItem children are both provided', async () => {
await expect(container.renderToString(MixedApiFixture)).rejects.toThrow(
'List: received both the `items` prop and ListItem children. Use one API or the other.'
)
})
})
53 changes: 37 additions & 16 deletions src/components/List/index.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
import { BuildError } from '@lib/errors/BuildError'
import AccentBorderLeftList from '@components/List/layouts/AccentBorderLeftList.astro'
import BadgeList from '@components/List/layouts/BadgeList.astro'
import CardGridList from '@components/List/layouts/CardGridList.astro'
Expand All @@ -14,9 +15,10 @@ import ThreeColumnIconList from '@components/List/layouts/ThreeColumnIconList.as
import TwoColumnCheckIconsList from '@components/List/layouts/TwoColumnCheckIconsList.astro'
import TwoColumnIconList from '@components/List/layouts/TwoColumnIconList.astro'
import ZebraList from '@components/List/layouts/ZebraList.astro'
import { getListItemsFromSlotMarkup } from '@components/List/server/slotItems'

export type Props = {
items: {
items?: {
title?: string
lead?: string
text: string
Expand Down Expand Up @@ -45,14 +47,33 @@ export type Props = {
style?: Record<string, unknown>
}

const { items, size, color, startNumber, variant = 'default', classes, style } = Astro.props
const { items, size, color, startNumber, variant = 'default', classes, style } = Astro.props as Props
const hasItemsProp = Object.prototype.hasOwnProperty.call(Astro.props, 'items')
const hasDefaultSlot = Astro.slots.has('default')

if (hasItemsProp && hasDefaultSlot) {
throw new BuildError(
'List: received both the `items` prop and ListItem children. Use one API or the other.',
{
phase: 'compilation',
filePath: 'src/components/List/index.astro',
tool: 'list',
}
)
}

const slotMarkup = hasDefaultSlot ? await Astro.slots.render('default') : ''
const normalizedItems = hasDefaultSlot
? getListItemsFromSlotMarkup(slotMarkup, variant)
: (items ?? [])

const classesProps = classes ? { classes } : {}
const numericSizeProps = typeof size === 'number' ? { size } : {}
const chatBubbleSizeProps = typeof size === 'string' ? { size } : {}
const colorProps = typeof color === 'string' && color.trim().length > 0 ? { color } : {}
const startNumberProps = typeof startNumber === 'number' ? { startNumber } : {}
const styleProps = style ? { style } : {}
const itemsWithColor = items as Array<{
const itemsWithColor = normalizedItems as Array<{
title?: string
lead?: string
text: string
Expand All @@ -61,32 +82,32 @@ const itemsWithColor = items as Array<{
inverseColor?: string
bgColor?: string
}>
const itemsWithIconAndColor = items as Array<{
const itemsWithIconAndColor = normalizedItems as Array<{
text: string
icon: string
color: string
inverseColor?: string
bgColor?: string
}>
const plainIconItems = items.filter((item): item is Props['items'][number] & { icon: string } => {
const plainIconItems = normalizedItems.filter((item): item is NonNullable<Props['items']>[number] & { icon: string } => {
return typeof item.icon === 'string' && item.icon.trim().length > 0
})
---

<div class:list={[classes?.wrapper ? classes.wrapper : 'mb-6']}>
{variant === 'accent-border-left-list' && <AccentBorderLeftList items={items} {...classesProps} {...styleProps} />}
{variant === 'badge-list' && <BadgeList items={items} {...classesProps} {...styleProps} />}
{variant === 'card-grid-list' && <CardGridList items={items} {...classesProps} {...styleProps} />}
{variant === 'chat-bubbles' && <ChatBubbles items={items} {...classesProps} {...chatBubbleSizeProps} />}
{variant === 'check-icons-list' && <CheckIconsList items={items} {...classesProps} {...styleProps} />}
{variant === 'chevron-list' && <ChevronList items={items} {...classesProps} {...styleProps} />}
{variant === 'accent-border-left-list' && <AccentBorderLeftList items={normalizedItems} {...classesProps} {...styleProps} />}
{variant === 'badge-list' && <BadgeList items={normalizedItems} {...classesProps} {...styleProps} />}
{variant === 'card-grid-list' && <CardGridList items={normalizedItems} {...classesProps} {...styleProps} />}
{variant === 'chat-bubbles' && <ChatBubbles items={normalizedItems} {...classesProps} {...chatBubbleSizeProps} />}
{variant === 'check-icons-list' && <CheckIconsList items={normalizedItems} {...classesProps} {...styleProps} />}
{variant === 'chevron-list' && <ChevronList items={normalizedItems} {...classesProps} {...styleProps} />}
{variant === 'colored-marker-list' && <ColoredMarkerList items={itemsWithColor} {...classesProps} {...numericSizeProps} {...styleProps} />}
{variant === 'numbered-with-background-list' && <NumberedWithBackgroundList items={items} {...classesProps} {...colorProps} {...startNumberProps} {...styleProps} />}
{variant === 'numbered-with-background-list' && <NumberedWithBackgroundList items={normalizedItems} {...classesProps} {...colorProps} {...startNumberProps} {...styleProps} />}
{variant === 'plain-icon-list' && <PlainIconList items={plainIconItems} {...classesProps} {...numericSizeProps} {...styleProps} />}
{variant === 'side-by-side-list' && <SideBySideList items={items} {...classesProps} {...styleProps} />}
{variant === 'timeline-list' && <TimelineList items={items} {...classesProps} {...styleProps} />}
{variant === 'two-column-check-icons-list' && <TwoColumnCheckIconsList items={items} {...classesProps} {...numericSizeProps} {...styleProps} />}
{variant === 'side-by-side-list' && <SideBySideList items={normalizedItems} {...classesProps} {...styleProps} />}
{variant === 'timeline-list' && <TimelineList items={normalizedItems} {...classesProps} {...styleProps} />}
{variant === 'two-column-check-icons-list' && <TwoColumnCheckIconsList items={normalizedItems} {...classesProps} {...numericSizeProps} {...styleProps} />}
{variant === 'two-column-icon-list' && <TwoColumnIconList items={itemsWithIconAndColor} {...classesProps} {...numericSizeProps} {...styleProps} />}
{variant === 'three-column-icon-list' && <ThreeColumnIconList items={itemsWithIconAndColor} {...classesProps} {...numericSizeProps} {...styleProps} />}
{variant === 'zebra-list' && <ZebraList items={items} {...classesProps} {...styleProps} />}
{variant === 'zebra-list' && <ZebraList items={normalizedItems} {...classesProps} {...styleProps} />}
</div>
2 changes: 1 addition & 1 deletion src/components/List/layouts/BadgeList.astro
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const emClass = ["text-content font-bold not-italic mr-2", classes?.em]
<li class:list={liClass}>
{item.title && (
<span class={titleCellClass}>
<span class:list={titleClass} set:html={item.title}></span>
<span class:list={["list-badge", titleClass]} set:html={item.title}></span>
</span>
)}
<span class:list={bodyClass}>
Expand Down
7 changes: 7 additions & 0 deletions src/components/List/server/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { isType1Element } from '@components/scripts/assertions/elements'

export const queryListItemElements = (context: ParentNode): Element[] => {
return Array.from(context.querySelectorAll('wsb-list-item')).filter((element): element is Element => {
return isType1Element(element) && element.tagName.toLowerCase() === 'wsb-list-item'
})
}
Loading
Loading