From 0f31fa84f808a88bf9817329da93cc27b77a366b Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Tue, 25 Aug 2026 14:35:46 -0500 Subject: [PATCH 1/6] Modernize Storybook component docs --- package-lock.json | 259 +------ packages/storybook/.storybook/preview.js | 6 +- packages/storybook/.storybook/theme.js | 4 + packages/storybook/package.json | 1 - .../src/nimble/button/button-anatomy.tsx | 31 + .../src/nimble/button/button-sections.ts | 18 + .../storybook/src/nimble/button/button.mdx | 210 +++++- .../nimble/button/images/button-anatomy.svg | 64 ++ .../storybook/src/nimble/dialog/dialog.mdx | 2 +- .../patterns/button/button-styling-docs.mdx | 276 +++---- .../storybook/src/nimble/select/select.mdx | 65 +- packages/storybook/src/nimble/table/table.mdx | 2 +- .../ok/ts/icon-dynamic/ts-icon-dynamic.mdx | 2 +- .../storybook/src/utilities/story-layout.tsx | 707 ++++++++++++++++-- packages/storybook/src/vite-env.d.ts | 4 + 15 files changed, 1157 insertions(+), 494 deletions(-) create mode 100644 packages/storybook/src/nimble/button/button-anatomy.tsx create mode 100644 packages/storybook/src/nimble/button/button-sections.ts create mode 100644 packages/storybook/src/nimble/button/images/button-anatomy.svg create mode 100644 packages/storybook/src/vite-env.d.ts diff --git a/package-lock.json b/package-lock.json index 119f0d0b8c..42ab5cd183 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9934,6 +9934,7 @@ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/unist": "*" } @@ -10060,13 +10061,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/prismjs": { - "version": "1.26.6", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", - "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/react": { "version": "19.2.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", @@ -12275,28 +12269,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/chardet": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", @@ -12666,17 +12638,6 @@ "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", "license": "Apache-2.0" }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/command-line-args": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.2.tgz", @@ -16637,6 +16598,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "peer": true, "engines": { "node": ">=0.4.x" } @@ -17189,38 +17151,6 @@ "node": ">= 0.4" } }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -17238,23 +17168,6 @@ "hermes-estree": "0.25.1" } }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, - "node_modules/highlightjs-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/hono": { "version": "4.12.32", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.32.tgz", @@ -17893,32 +17806,6 @@ "node": ">= 0.10" } }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -18130,17 +18017,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -18240,17 +18116,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -20511,35 +20376,6 @@ "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "license": "MIT" }, - "node_modules/lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lowlight/node_modules/fault": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", - "dev": true, - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -26347,26 +26183,6 @@ "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", "dev": true }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/parse-imports-exports": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", @@ -26947,16 +26763,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "license": "MIT" }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/probe-image-size": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.3.0.tgz", @@ -27080,17 +26886,6 @@ "react-is": "^16.13.1" } }, - "node_modules/property-information": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", - "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/prosemirror-changeset": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.1.tgz", @@ -27500,27 +27295,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-syntax-highlighter": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz", - "integrity": "sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "highlight.js": "^10.4.1", - "highlightjs-vue": "^1.0.0", - "lowlight": "^1.17.0", - "prismjs": "^1.30.0", - "refractor": "^5.0.0" - }, - "engines": { - "node": ">= 16.20.2" - }, - "peerDependencies": { - "react": ">= 0.14.0" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -27627,23 +27401,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/refractor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", - "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/prismjs": "^1.0.0", - "hastscript": "^9.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -30181,17 +29938,6 @@ "node": ">=0.10.0" } }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", @@ -33752,7 +33498,6 @@ "@storybook/html-vite": "^10.3.6", "@types/js-beautify": "^1.14.3", "js-beautify": "^2.0.3", - "react-syntax-highlighter": "^16.1.0", "remark-gfm": "^4.0.0", "storybook": "^10.3.6", "storybook-addon-pseudo-states": "^10.3.6", diff --git a/packages/storybook/.storybook/preview.js b/packages/storybook/.storybook/preview.js index c111457bc1..72b76015a6 100644 --- a/packages/storybook/.storybook/preview.js +++ b/packages/storybook/.storybook/preview.js @@ -1,6 +1,4 @@ import { configureActions } from 'storybook/actions'; -import { SyntaxHighlighter } from 'storybook/internal/components'; -import csharp from 'react-syntax-highlighter/dist/esm/languages/prism/csharp'; import '@ni/nimble-tokens/dist/fonts/css/fonts.css'; import './preview.css'; import { @@ -9,8 +7,6 @@ import { } from '../src/utilities/states'; import { theme } from './theme'; -SyntaxHighlighter.registerLanguage('cs', csharp); - const parameters = { backgrounds: { options: backgroundStates.reduce((obj, curr) => { @@ -57,7 +53,7 @@ const parameters = { }, docs: { controls: { sort: 'alpha' }, - toc: { headingSelector: 'h1, h2, h3' }, + toc: { disable: true }, theme } }; diff --git a/packages/storybook/.storybook/theme.js b/packages/storybook/.storybook/theme.js index 448ba91ff2..20a88fbb96 100644 --- a/packages/storybook/.storybook/theme.js +++ b/packages/storybook/.storybook/theme.js @@ -3,6 +3,8 @@ import { tokenValues } from '@ni/nimble-components/dist/esm/theme-provider/desig import logoLight from './nimble-light.svg'; import logoDark from './nimble-dark.svg'; +const fontFamily = family => `"${family}", sans-serif`; + const light = create({ base: 'light', @@ -23,6 +25,7 @@ const light = create({ appPreviewBg: tokenValues.applicationBackgroundColor.light, appBorderColor: tokenValues.tableRowBorderColor.light, appBorderRadius: 1, + fontBase: fontFamily(tokenValues.bodyFontFamily), // Text colors textColor: tokenValues.bodyFontColor.light, @@ -66,6 +69,7 @@ const dark = create({ appPreviewBg: tokenValues.applicationBackgroundColor.dark, appBorderColor: tokenValues.tableRowBorderColor.dark, appBorderRadius: 1, + fontBase: fontFamily(tokenValues.bodyFontFamily), // Text colors textColor: tokenValues.bodyFontColor.dark, diff --git a/packages/storybook/package.json b/packages/storybook/package.json index d3f79e255a..42cf0acc36 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -42,7 +42,6 @@ "@storybook/html-vite": "^10.3.6", "@types/js-beautify": "^1.14.3", "js-beautify": "^2.0.3", - "react-syntax-highlighter": "^16.1.0", "remark-gfm": "^4.0.0", "storybook": "^10.3.6", "storybook-addon-pseudo-states": "^10.3.6", diff --git a/packages/storybook/src/nimble/button/button-anatomy.tsx b/packages/storybook/src/nimble/button/button-anatomy.tsx new file mode 100644 index 0000000000..0147dc1cc1 --- /dev/null +++ b/packages/storybook/src/nimble/button/button-anatomy.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { + applicationBackgroundColor, + bodyFontColor, + buttonAccentBlockFontColor, + tagFillColor +} from '@ni/nimble-components/dist/esm/theme-provider/design-tokens'; +import anatomySvg from './images/button-anatomy.svg?raw'; + +const anatomySvgStyle = { + display: 'block', + maxWidth: '100%', + paddingBottom: '16px', + '--button-anatomy-background-color': `var(${applicationBackgroundColor.cssCustomProperty})`, + '--button-anatomy-body-color': `var(${bodyFontColor.cssCustomProperty})`, + '--button-anatomy-tag-color': `var(${tagFillColor.cssCustomProperty})`, + '--button-anatomy-accent-color': `var(${buttonAccentBlockFontColor.cssCustomProperty})` +} as React.CSSProperties; + +interface ButtonAnatomyProps { + alt: string; +} + +export const ButtonAnatomy = ({ alt }: ButtonAnatomyProps): React.JSX.Element => { + return
; +}; diff --git a/packages/storybook/src/nimble/button/button-sections.ts b/packages/storybook/src/nimble/button/button-sections.ts new file mode 100644 index 0000000000..7bc9075bfd --- /dev/null +++ b/packages/storybook/src/nimble/button/button-sections.ts @@ -0,0 +1,18 @@ +export const buttonSections = { + overview: { id: 'overview', title: 'Overview', level: 2 }, + buttonAnatomy: { id: 'button-anatomy', title: 'Button Anatomy', level: 2 }, + implementationApi: { id: 'implementation-api', title: 'Implementation & API', level: 2 }, + usage: { id: 'usage', title: 'Usage', level: 2 }, + whenToUse: { id: 'when-to-use', title: 'When to Use', level: 3 }, + appearanceStyling: { id: 'appearance-styling', title: 'Appearance & Styling', level: 2 }, + appearances: { id: 'appearances', title: 'Appearances', level: 3 }, + appearanceVariants: { id: 'appearance-variants', title: 'Appearance Variants', level: 3 }, + sizing: { id: 'sizing', title: 'Sizing', level: 3 }, + spacingWithOtherComponents: { id: 'spacing-with-other-components', title: 'Spacing with Other Components', level: 3 }, + buttonGuidelines: { id: 'button-guidelines', title: 'Button Guidelines', level: 2 }, + accessibility: { id: 'accessibility', title: 'Accessibility', level: 2 }, + accessibleIconButtons: { id: 'accessible-icon-buttons', title: 'Accessible Icon Buttons', level: 3 }, + keyboardUsage: { id: 'keyboard-usage', title: 'Keyboard Usage', level: 3 }, + alternativesRelated: { id: 'alternatives-related', title: 'Alternatives & Related', level: 2 }, + alternativesToConsider: { id: 'alternatives-to-consider', title: 'Alternatives to Consider', level: 3 } +} as const; diff --git a/packages/storybook/src/nimble/button/button.mdx b/packages/storybook/src/nimble/button/button.mdx index c4f83de384..54bbe9ae66 100644 --- a/packages/storybook/src/nimble/button/button.mdx +++ b/packages/storybook/src/nimble/button/button.mdx @@ -1,46 +1,182 @@ import { Canvas, Meta, Controls, Title } from '@storybook/addon-docs/blocks'; import ContentHiddenDocs from '../patterns/button/content-hidden-docs.mdx'; import ButtonStylingDocs from '../patterns/button/button-styling-docs.mdx'; +import { buttonSections as sections } from './button-sections'; import ComponentApisLink from '../../docs/component-apis-link.mdx'; import { buttonTag } from '@ni/nimble-components/dist/esm/button'; import { anchorButtonTag } from '@ni/nimble-components/dist/esm/anchor-button'; +import { NimbleButton } from '@ni/nimble-react/button'; import * as buttonStories from './button.stories'; -import { Tag } from '../../utilities/story-layout'; +import { ButtonAnatomy } from './button-anatomy'; +import { + Do, + Dont, + StoryAnatomy, + StoryApi, + StoryGuidance, + StoryGuidanceGrid, + StoryPage, + StoryPreview, + StorySection, + Tag +} from '../../utilities/story-layout'; - - -Per [W3C](https://www.w3.org/WAI/ARIA/apg/patterns/button/) - A button is a -widget that enables users to trigger an action or event, such as submitting a -form, opening a dialog, canceling an action, or performing a delete operation. - -Consider <Tag name={anchorButtonTag} /> if you want a button that triggers -navigation to a URL. - -<Canvas of={buttonStories.button} /> - -## API - -<Controls of={buttonStories.button} /> - -<ComponentApisLink /> - -## Styling - -<ButtonStylingDocs components={{ Button: buttonTag }} /> - -### Sizing - -Nimble Buttons are currently always 32px tall. Designs exist for other sizes; if -you need these in an application, please comment on -[Configurable height for nimble controls (#610)](https://github.com/ni/nimble/issues/610). - -{/* ## Usage */} - -{/* ## Examples */} - -## Accessibility - -<ContentHiddenDocs buttonElement="nimble-button" /> - -{/* ## Resources */} +<StoryPage toc={[ + sections.overview, + sections.buttonAnatomy, + sections.implementationApi, + sections.usage, + sections.whenToUse, + sections.appearanceStyling, + sections.appearances, + sections.appearanceVariants, + sections.sizing, + sections.spacingWithOtherComponents, + sections.buttonGuidelines, + sections.accessibility, + sections.accessibleIconButtons, + sections.keyboardUsage, + sections.alternativesRelated, + sections.alternativesToConsider + ]}> + <Title of={buttonStories} /> + + <StorySection {...sections.overview}> + <div> + Per [W3C](https://www.w3.org/WAI/ARIA/apg/patterns/button/), a + button is a widget that enables users to trigger an action or event, + such as submitting a form, opening a dialog, canceling an action, or + performing a delete operation. + </div> + + <div> + Consider <Tag name={anchorButtonTag} /> if you want a button that + triggers navigation to a URL. + </div> + </StorySection> + + <StorySection {...sections.buttonAnatomy}> + <StoryAnatomy alt="Button anatomy showing the Container, Icon, and Label parts of a button."> + <ButtonAnatomy alt="Button anatomy showing the Container, Icon, and Label parts of a button." /> + </StoryAnatomy> + </StorySection> + + <StorySection {...sections.implementationApi}> + <StoryPreview> + <Canvas of={buttonStories.button} /> + </StoryPreview> + <StoryApi> + <Controls of={buttonStories.button} /> + <ComponentApisLink /> + </StoryApi> + </StorySection> + + <StorySection {...sections.usage}> + <h3 id={sections.whenToUse.id}>{sections.whenToUse.title}</h3> + <div> + <strong>Primary Actions:</strong> Use buttons for primary actions + that users need to take, such as submitting a form, saving changes, + or proceeding to the next step. + </div> + + <div> + <strong>Call to Action (CTA):</strong> Buttons are ideal for CTAs + that encourage users to take a specific action, like "Sign Up," + "Buy Now," or "Learn More." + </div> + </StorySection> + + <StorySection {...sections.appearanceStyling}> + <div> + Standard styling options for the button control are found below, in + order of usage from least to most prominent. Consider the indented + context before employing an appearance variant. + </div> + <ButtonStylingDocs components={{ Button: buttonTag }} /> + + <h3 id={sections.sizing.id}>{sections.sizing.title}</h3> + <div> + Nimble Buttons are currently always 32px tall. Designs exist for + other sizes; if you need these in an application, please comment on + [Configurable height for nimble controls (#610)](https://github.com/ni/nimble/issues/610). + </div> + <h3 id={sections.spacingWithOtherComponents.id}>{sections.spacingWithOtherComponents.title}</h3> + <div> + Use consistent spacing between elements to keep layouts clean and + organized. Adequate padding around components prevents visual + clutter and helps users focus on key information. Use responsive + spacing when layouts change across screen sizes. + </div> + </StorySection> + + <StorySection {...sections.buttonGuidelines}> + <StoryGuidanceGrid> + <Do + title="Create a clear action hierarchy" + example={ + <div className="story-guidance-actions"> + <NimbleButton appearance="ghost">Cancel</NimbleButton> + <NimbleButton appearance="block">Save</NimbleButton> + </div> + } + > + Use one visually prominent action per section and pair it with + less prominent actions when users need a clear hierarchy. + </Do> + <Dont + title="Prioritize one prominent action" + example={ + <div className="story-guidance-actions"> + <NimbleButton appearance="block">Save</NimbleButton> + <NimbleButton appearance="block">Submit</NimbleButton> + </div> + } + > + Do not use multiple visually prominent buttons together when + one action should take priority. + </Dont> + <Do + title="Use concise labels" + example={ + <div className="story-guidance-actions"> + <NimbleButton appearance="outline">Save Changes</NimbleButton> + </div> + } + > + Use concise labels that describe the action the button performs. + </Do> + <Dont + title="Avoid vague labels" + example={ + <div className="story-guidance-actions"> + <NimbleButton appearance="outline">Click Here</NimbleButton> + </div> + } + > + Avoid vague labels that do not tell users what will happen when + the button is activated. + </Dont> + </StoryGuidanceGrid> + </StorySection> + + <StorySection {...sections.accessibility}> + <StoryGuidance> + <h3 id={sections.accessibleIconButtons.id}>{sections.accessibleIconButtons.title}</h3> + <ContentHiddenDocs buttonElement="nimble-button" /> + <h3 id={sections.keyboardUsage.id}>{sections.keyboardUsage.title}</h3> + <div> + Buttons must remain keyboard accessible so users can move focus + to them and activate them with standard keyboard controls. + </div> + </StoryGuidance> + </StorySection> + + <StorySection {...sections.alternativesRelated}> + <h3 id={sections.alternativesToConsider.id}>{sections.alternativesToConsider.title}</h3> + <div> + Consider <Tag name={anchorButtonTag} /> if you need a button that + triggers navigation to a URL. + </div> + </StorySection> +</StoryPage> diff --git a/packages/storybook/src/nimble/button/images/button-anatomy.svg b/packages/storybook/src/nimble/button/images/button-anatomy.svg new file mode 100644 index 0000000000..976d29419a --- /dev/null +++ b/packages/storybook/src/nimble/button/images/button-anatomy.svg @@ -0,0 +1,64 @@ +<svg width="624" height="275" viewBox="32 50 624 275" fill="none" style="display: block; max-width: 100%; height: auto;" + xmlns="http://www.w3.org/2000/svg"> + <rect x="32" y="50" width="624" height="275" fill="var(--button-anatomy-background-color)"/> + <g id="Button"> + <rect width="1000" height="400" rx="2" fill="var(--button-anatomy-background-color)"/> + <g id="Container"> + <path id="Header" d="M34.814 25.978C33.764 25.978 33.176 25.117 33.176 23.647C33.176 21.82 34.058 20.434 35.78 19.531L36.368 20.602C35.213 21.274 34.625 22.03 34.625 23.248C34.688 23.227 34.751 23.227 34.814 23.227C35.57 23.227 36.2 23.71 36.2 24.529C36.2 25.411 35.612 25.978 34.814 25.978ZM39.3162 34V20.266H43.7052C46.5402 20.266 48.6192 21.127 48.6192 23.647C48.6192 24.949 47.9052 26.209 46.7082 26.629V26.713C48.2202 27.049 49.3332 28.12 49.3332 29.968C49.3332 32.719 47.0862 34 44.0622 34H39.3162ZM41.7522 25.936H43.4952C45.4062 25.936 46.2252 25.18 46.2252 24.004C46.2252 22.66 45.3222 22.156 43.5372 22.156H41.7522V25.936ZM41.7522 32.11H43.8102C45.8262 32.11 46.9602 31.375 46.9602 29.842C46.9602 28.393 45.8682 27.763 43.8102 27.763H41.7522V32.11ZM54.4925 34.252C52.2875 34.252 51.3215 32.761 51.3215 30.157V23.689H53.7365V29.842C53.7365 31.543 54.2195 32.173 55.3325 32.173C56.2355 32.173 56.8445 31.753 57.6215 30.745V23.689H60.0365V34H58.0625L57.8735 32.488H57.8105C56.9075 33.559 55.8995 34.252 54.4925 34.252ZM66.5184 34.252C64.1244 34.252 63.2424 32.74 63.2424 30.514V25.6H61.7724V23.794L63.3684 23.689L63.6624 20.875H65.6784V23.689H68.3034V25.6H65.6784V30.514C65.6784 31.732 66.1404 32.32 67.1274 32.32C67.4844 32.32 67.9044 32.215 68.1984 32.089L68.6184 33.874C68.0514 34.063 67.3374 34.252 66.5184 34.252ZM73.4862 34.252C71.0922 34.252 70.2102 32.74 70.2102 30.514V25.6H68.7402V23.794L70.3362 23.689L70.6302 20.875H72.6462V23.689H75.2712V25.6H72.6462V30.514C72.6462 31.732 73.1082 32.32 74.0952 32.32C74.4522 32.32 74.8722 32.215 75.1662 32.089L75.5862 33.874C75.0192 34.063 74.3052 34.252 73.4862 34.252ZM81.1239 34.252C78.5619 34.252 76.2309 32.278 76.2309 28.855C76.2309 25.411 78.5619 23.437 81.1239 23.437C83.7069 23.437 86.0379 25.411 86.0379 28.855C86.0379 32.278 83.7069 34.252 81.1239 34.252ZM81.1239 32.278C82.6359 32.278 83.5599 30.913 83.5599 28.855C83.5599 26.776 82.6359 25.411 81.1239 25.411C79.6119 25.411 78.7089 26.776 78.7089 28.855C78.7089 30.913 79.6119 32.278 81.1239 32.278ZM88.2183 34V23.689H90.2133L90.3813 25.075H90.4653C91.3893 24.172 92.4393 23.437 93.8463 23.437C96.0513 23.437 97.0173 24.928 97.0173 27.532V34H94.6023V27.847C94.6023 26.146 94.1193 25.516 92.9853 25.516C92.0823 25.516 91.4943 25.957 90.6333 26.797V34H88.2183ZM99.8111 25.789L99.2231 24.718C100.378 24.046 100.966 23.29 100.966 22.072C100.903 22.093 100.84 22.093 100.777 22.093C100.042 22.093 99.4121 21.61 99.4121 20.791C99.4121 19.909 99.9791 19.342 100.777 19.342C101.848 19.342 102.436 20.203 102.436 21.673C102.436 23.5 101.554 24.886 99.8111 25.789ZM111.94 26.692L111.457 28.372H114.964L114.481 26.692C114.04 25.243 113.641 23.668 113.242 22.156H113.158C112.78 23.689 112.381 25.243 111.94 26.692ZM107.341 34L111.814 20.266H114.67L119.143 34H116.581L115.51 30.283H110.89L109.819 34H107.341ZM120.416 34V23.689H122.411L122.579 25.075H122.663C123.587 24.172 124.637 23.437 126.044 23.437C128.249 23.437 129.215 24.928 129.215 27.532V34H126.8V27.847C126.8 26.146 126.317 25.516 125.183 25.516C124.28 25.516 123.692 25.957 122.831 26.797V34H120.416ZM134.456 34.252C132.65 34.252 131.411 33.034 131.411 31.228C131.411 29.023 133.238 27.868 137.48 27.406C137.438 26.314 136.997 25.39 135.59 25.39C134.582 25.39 133.616 25.852 132.692 26.398L131.81 24.781C132.944 24.067 134.414 23.437 136.052 23.437C138.614 23.437 139.895 25.012 139.895 27.889V34H137.9L137.732 32.866H137.648C136.724 33.643 135.674 34.252 134.456 34.252ZM135.233 32.362C136.073 32.362 136.703 31.942 137.48 31.228V28.918C134.666 29.275 133.742 30.01 133.742 31.039C133.742 31.963 134.372 32.362 135.233 32.362ZM145.88 34.252C143.486 34.252 142.604 32.74 142.604 30.514V25.6H141.134V23.794L142.73 23.689L143.024 20.875H145.04V23.689H147.665V25.6H145.04V30.514C145.04 31.732 145.502 32.32 146.489 32.32C146.846 32.32 147.266 32.215 147.56 32.089L147.98 33.874C147.413 34.063 146.699 34.252 145.88 34.252ZM153.518 34.252C150.956 34.252 148.625 32.278 148.625 28.855C148.625 25.411 150.956 23.437 153.518 23.437C156.101 23.437 158.432 25.411 158.432 28.855C158.432 32.278 156.101 34.252 153.518 34.252ZM153.518 32.278C155.03 32.278 155.954 30.913 155.954 28.855C155.954 26.776 155.03 25.411 153.518 25.411C152.006 25.411 151.103 26.776 151.103 28.855C151.103 30.913 152.006 32.278 153.518 32.278ZM160.613 34V23.689H162.608L162.776 25.096H162.86C163.721 24.172 164.729 23.437 166.01 23.437C167.522 23.437 168.383 24.13 168.866 25.285C169.853 24.235 170.882 23.437 172.184 23.437C174.368 23.437 175.376 24.928 175.376 27.532V34H172.94V27.847C172.94 26.146 172.436 25.516 171.365 25.516C170.714 25.516 170 25.936 169.202 26.797V34H166.787V27.847C166.787 26.146 166.283 25.516 165.191 25.516C164.561 25.516 163.826 25.936 163.028 26.797V34H160.613ZM178.794 38.242C178.227 38.242 177.828 38.158 177.45 38.032L177.891 36.142C178.08 36.205 178.374 36.289 178.626 36.289C179.718 36.289 180.348 35.554 180.684 34.525L180.873 33.874L176.82 23.689H179.277L181.062 28.75C181.356 29.653 181.65 30.682 181.965 31.648H182.049C182.301 30.703 182.574 29.674 182.826 28.75L184.38 23.689H186.711L182.973 34.462C182.112 36.793 181.041 38.242 178.794 38.242Z" fill="var(--button-anatomy-body-color)"/> + <g id="Anatomy"> + <g id="Diagram (66%)"> + <g id="nimble-action-button"> + <rect x="289" y="134.5" width="110" height="31" stroke="var(--button-anatomy-body-color)" stroke-opacity="0.3"/> + <g id="Focus Frame"> + <g id="Elements Frame"> + <g id="arrow-rotate-right_16x16"> + <path id="Vector" d="M313.4 144.77V146.48C315.22 146.48 316.7 148.02 316.7 149.91C316.7 151.8 315.22 153.34 313.4 153.34C311.58 153.34 310.14 151.84 310.11 149.97H310.21L312.47 150.87L309.41 144.77L306.35 150.88L308.46 150.07C308.54 152.84 310.72 155.06 313.4 155.06C316.08 155.06 318.35 152.76 318.35 149.92C318.35 147.08 316.13 144.78 313.4 144.78V144.77Z" fill="var(--button-anatomy-body-color)"/> + </g> + <path id="Text button" d="M328.373 155.168C327.757 155.168 327.16 155.051 326.581 154.818C326.012 154.585 325.508 154.253 325.069 153.824L326.021 152.718C326.348 153.026 326.721 153.278 327.141 153.474C327.561 153.661 327.981 153.754 328.401 153.754C328.924 153.754 329.32 153.647 329.591 153.432C329.862 153.217 329.997 152.933 329.997 152.578C329.997 152.195 329.862 151.92 329.591 151.752C329.33 151.584 328.994 151.411 328.583 151.234L327.323 150.688C327.024 150.557 326.73 150.389 326.441 150.184C326.161 149.979 325.928 149.717 325.741 149.4C325.564 149.083 325.475 148.7 325.475 148.252C325.475 147.767 325.606 147.333 325.867 146.95C326.138 146.558 326.502 146.25 326.959 146.026C327.426 145.793 327.958 145.676 328.555 145.676C329.087 145.676 329.6 145.783 330.095 145.998C330.59 146.203 331.014 146.483 331.369 146.838L330.543 147.874C330.254 147.631 329.946 147.44 329.619 147.3C329.302 147.16 328.947 147.09 328.555 147.09C328.126 147.09 327.776 147.188 327.505 147.384C327.244 147.571 327.113 147.832 327.113 148.168C327.113 148.401 327.178 148.597 327.309 148.756C327.449 148.905 327.631 149.036 327.855 149.148C328.079 149.251 328.317 149.353 328.569 149.456L329.815 149.974C330.356 150.207 330.8 150.515 331.145 150.898C331.49 151.271 331.663 151.789 331.663 152.452C331.663 152.947 331.532 153.399 331.271 153.81C331.01 154.221 330.632 154.552 330.137 154.804C329.652 155.047 329.064 155.168 328.373 155.168ZM333.65 157.828C333.463 157.828 333.3 157.814 333.16 157.786C333.02 157.758 332.885 157.725 332.754 157.688L333.048 156.428C333.113 156.447 333.188 156.465 333.272 156.484C333.365 156.512 333.454 156.526 333.538 156.526C333.902 156.526 334.191 156.414 334.406 156.19C334.63 155.975 334.798 155.695 334.91 155.35L335.036 154.916L332.334 148.126H333.972L335.162 151.5C335.265 151.799 335.363 152.116 335.456 152.452C335.559 152.779 335.661 153.105 335.764 153.432H335.82C335.904 153.115 335.988 152.793 336.072 152.466C336.165 152.13 336.254 151.808 336.338 151.5L337.374 148.126H338.928L336.436 155.308C336.147 156.083 335.797 156.694 335.386 157.142C334.975 157.599 334.397 157.828 333.65 157.828ZM340.12 155V148.126H341.45L341.562 149.05H341.618C341.926 148.751 342.262 148.495 342.626 148.28C342.99 148.065 343.405 147.958 343.872 147.958C344.609 147.958 345.146 148.196 345.482 148.672C345.818 149.148 345.986 149.82 345.986 150.688V155H344.376V150.898C344.376 150.329 344.292 149.927 344.124 149.694C343.956 149.461 343.68 149.344 343.298 149.344C342.999 149.344 342.733 149.419 342.5 149.568C342.276 149.708 342.019 149.918 341.73 150.198V155H340.12ZM350.824 155.168C350.198 155.168 349.634 155.028 349.13 154.748C348.635 154.459 348.238 154.048 347.94 153.516C347.65 152.975 347.506 152.326 347.506 151.57C347.506 150.805 347.664 150.156 347.982 149.624C348.299 149.083 348.719 148.672 349.242 148.392C349.774 148.103 350.343 147.958 350.95 147.958C351.388 147.958 351.771 148.033 352.098 148.182C352.424 148.331 352.714 148.513 352.966 148.728L352.182 149.778C352.004 149.619 351.822 149.498 351.636 149.414C351.449 149.321 351.248 149.274 351.034 149.274C350.483 149.274 350.03 149.484 349.676 149.904C349.33 150.324 349.158 150.879 349.158 151.57C349.158 152.251 349.326 152.802 349.662 153.222C350.007 153.642 350.45 153.852 350.992 153.852C351.262 153.852 351.514 153.796 351.748 153.684C351.99 153.563 352.21 153.423 352.406 153.264L353.064 154.328C352.746 154.608 352.392 154.818 352 154.958C351.608 155.098 351.216 155.168 350.824 155.168ZM357.363 155V145.844H359.029L361.885 150.996L362.823 152.942H362.879C362.842 152.475 362.795 151.967 362.739 151.416C362.693 150.865 362.669 150.333 362.669 149.82V145.844H364.223V155H362.557L359.701 149.848L358.763 147.902H358.707C358.745 148.387 358.787 148.896 358.833 149.428C358.889 149.96 358.917 150.483 358.917 150.996V155H357.363ZM369.225 155.168C368.655 155.168 368.123 155.028 367.629 154.748C367.134 154.459 366.733 154.048 366.425 153.516C366.117 152.975 365.963 152.326 365.963 151.57C365.963 150.805 366.117 150.156 366.425 149.624C366.733 149.083 367.134 148.672 367.629 148.392C368.123 148.103 368.655 147.958 369.225 147.958C369.803 147.958 370.34 148.103 370.835 148.392C371.329 148.672 371.731 149.083 372.039 149.624C372.347 150.156 372.501 150.805 372.501 151.57C372.501 152.326 372.347 152.975 372.039 153.516C371.731 154.048 371.329 154.459 370.835 154.748C370.34 155.028 369.803 155.168 369.225 155.168ZM369.225 153.852C369.729 153.852 370.125 153.642 370.415 153.222C370.704 152.802 370.849 152.251 370.849 151.57C370.849 150.879 370.704 150.324 370.415 149.904C370.125 149.484 369.729 149.274 369.225 149.274C368.721 149.274 368.324 149.484 368.035 149.904C367.755 150.324 367.615 150.879 367.615 151.57C367.615 152.251 367.755 152.802 368.035 153.222C368.324 153.642 368.721 153.852 369.225 153.852ZM375.146 155L373.326 148.126H374.95L375.776 151.696C375.851 152.023 375.912 152.349 375.958 152.676C376.014 153.003 376.075 153.334 376.14 153.67H376.196C376.262 153.334 376.327 153.003 376.392 152.676C376.467 152.34 376.546 152.013 376.63 151.696L377.526 148.126H378.996L379.906 151.696C379.99 152.023 380.07 152.349 380.144 152.676C380.219 153.003 380.294 153.334 380.368 153.67H380.424C380.49 153.334 380.55 153.003 380.606 152.676C380.662 152.349 380.723 152.023 380.788 151.696L381.614 148.126H383.126L381.376 155H379.458L378.66 151.794C378.586 151.467 378.516 151.145 378.45 150.828C378.385 150.501 378.315 150.156 378.24 149.792H378.184C378.119 150.156 378.054 150.501 377.988 150.828C377.923 151.155 377.853 151.481 377.778 151.808L376.994 155H375.146Z" fill="var(--button-anatomy-body-color)"/> + </g> + </g> + </g> + <g id="Anatomy_2"> + <g id="Callout"> + <g id="Top"> + <g id="CrossBar"> + <path id="Line" d="M371 158V159H339V158H371Z" fill="var(--button-anatomy-body-color)" fill-opacity="0.6"/> + <path id="Line_2" d="M354.5 159L355.5 159L355.5 178L354.5 178L354.5 159Z" fill="var(--button-anatomy-body-color)" fill-opacity="0.6"/> + </g> + <g id="Pill Label"> + <rect x="344" y="178" width="22" height="15" rx="7.5" fill="var(--button-anatomy-tag-color)"/> + <path id="#" d="M354.798 189.144C354.358 189.144 353.962 189.096 353.61 189C353.266 188.904 352.958 188.776 352.686 188.616C352.414 188.448 352.186 188.264 352.002 188.064L352.65 187.176C352.906 187.408 353.202 187.608 353.538 187.776C353.882 187.936 354.262 188.016 354.678 188.016C354.99 188.016 355.27 187.972 355.518 187.884C355.766 187.788 355.958 187.656 356.094 187.488C356.238 187.312 356.31 187.104 356.31 186.864C356.31 186.6 356.23 186.372 356.07 186.18C355.91 185.988 355.646 185.84 355.278 185.736C354.91 185.624 354.41 185.568 353.778 185.568V184.56C354.33 184.56 354.77 184.508 355.098 184.404C355.426 184.292 355.666 184.148 355.818 183.972C355.97 183.788 356.046 183.576 356.046 183.336C356.046 183.016 355.922 182.768 355.674 182.592C355.434 182.408 355.11 182.316 354.702 182.316C354.374 182.316 354.066 182.384 353.778 182.52C353.49 182.648 353.218 182.82 352.962 183.036L352.242 182.184C352.594 181.896 352.978 181.664 353.394 181.488C353.818 181.312 354.274 181.224 354.762 181.224C355.282 181.224 355.742 181.304 356.142 181.464C356.55 181.616 356.866 181.844 357.09 182.148C357.322 182.444 357.438 182.808 357.438 183.24C357.438 183.664 357.298 184.02 357.018 184.308C356.738 184.596 356.362 184.824 355.89 184.992V185.04C356.226 185.12 356.53 185.244 356.802 185.412C357.082 185.58 357.302 185.792 357.462 186.048C357.622 186.304 357.702 186.6 357.702 186.936C357.702 187.4 357.566 187.8 357.294 188.136C357.03 188.464 356.678 188.716 356.238 188.892C355.806 189.06 355.326 189.144 354.798 189.144Z" fill="var(--button-anatomy-accent-color)"/> + </g> + </g> + </g> + <g id="Callout_2"> + <g id="Top_2"> + <path id="Line_3" d="M312.5 158L313.5 158L313.5 178L312.5 178L312.5 158Z" fill="var(--button-anatomy-body-color)" fill-opacity="0.6"/> + <g id="Pill Label_2"> + <rect x="302" y="178" width="22" height="15" rx="7.5" fill="var(--button-anatomy-tag-color)"/> + <path id="#_2" d="M310.182 189V188.196C310.982 187.508 311.666 186.896 312.234 186.36C312.81 185.824 313.25 185.336 313.554 184.896C313.866 184.448 314.022 184.028 314.022 183.636C314.022 183.228 313.898 182.908 313.65 182.676C313.402 182.436 313.038 182.316 312.558 182.316C312.23 182.316 311.926 182.4 311.646 182.568C311.366 182.736 311.11 182.936 310.878 183.168L310.122 182.4C310.49 182.024 310.878 181.736 311.286 181.536C311.694 181.328 312.178 181.224 312.738 181.224C313.266 181.224 313.722 181.32 314.106 181.512C314.498 181.704 314.802 181.976 315.018 182.328C315.234 182.68 315.342 183.092 315.342 183.564C315.342 184.012 315.202 184.472 314.922 184.944C314.642 185.408 314.262 185.884 313.782 186.372C313.31 186.86 312.778 187.372 312.186 187.908C312.41 187.884 312.646 187.864 312.894 187.848C313.142 187.832 313.37 187.824 313.578 187.824H315.738V189H310.182Z" fill="var(--button-anatomy-accent-color)"/> + </g> + </g> + </g> + <g id="Callout_3"> + <g id="Right"> + <g id="Pill Label_3"> + <rect x="248" y="143" width="22" height="15" rx="7.5" fill="var(--button-anatomy-tag-color)"/> + <path id="#_3" d="M256.458 154V152.872H258.582V147.856H256.89V146.992C257.21 146.944 257.49 146.892 257.73 146.836C257.97 146.78 258.186 146.712 258.378 146.632C258.578 146.552 258.77 146.464 258.954 146.368H259.962V152.872H261.834V154H256.458Z" fill="var(--button-anatomy-accent-color)"/> + </g> + <g id="Ellipse"> + <g id="Dot"> + <path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M298 150.5C298 151.881 296.88 153 295.5 153C294.29 153 293.282 152.141 293.05 151L270 151L270 150L293.05 150C293.282 148.859 294.29 148 295.5 148C296.88 148 298 149.119 298 150.5ZM297 150.5C297 149.672 296.328 149 295.5 149C294.671 149 294 149.672 294 150.5C294 151.328 294.671 152 295.5 152C296.328 152 297 151.328 297 150.5Z" fill="var(--button-anatomy-body-color)" fill-opacity="0.6"/> + </g> + </g> + </g> + </g> + </g> + </g> + <g id="Labels"> + <path id="Container Icon Label" d="M55.2413 278V276.496H57.5133V269.808H55.6413V268.656C56.1427 268.56 56.58 268.448 56.9533 268.32C57.3267 268.181 57.6733 268.016 57.9933 267.824H59.3693V276.496H61.3693V278H55.2413ZM64.4685 278.192C64.1378 278.192 63.8551 278.075 63.6205 277.84C63.3858 277.605 63.2685 277.307 63.2685 276.944C63.2685 276.581 63.3858 276.283 63.6205 276.048C63.8551 275.813 64.1378 275.696 64.4685 275.696C64.8098 275.696 65.0925 275.813 65.3165 276.048C65.5405 276.283 65.6525 276.581 65.6525 276.944C65.6525 277.307 65.5405 277.605 65.3165 277.84C65.0925 278.075 64.8098 278.192 64.4685 278.192ZM77.504 278.192C76.6293 278.192 75.8347 277.984 75.12 277.568C74.4053 277.152 73.8347 276.544 73.408 275.744C72.992 274.933 72.784 273.952 72.784 272.8C72.784 271.659 72.9973 270.683 73.424 269.872C73.8613 269.051 74.4427 268.427 75.168 268C75.8933 267.563 76.6933 267.344 77.568 267.344C78.2293 267.344 78.816 267.477 79.328 267.744C79.84 268.011 80.2613 268.32 80.592 268.672L79.6 269.872C79.3333 269.595 79.0347 269.376 78.704 269.216C78.384 269.045 78.0213 268.96 77.616 268.96C77.0507 268.96 76.544 269.115 76.096 269.424C75.6587 269.723 75.312 270.155 75.056 270.72C74.8107 271.285 74.688 271.963 74.688 272.752C74.688 273.947 74.9493 274.885 75.472 275.568C75.9947 276.24 76.6933 276.576 77.568 276.576C78.0373 276.576 78.4533 276.48 78.816 276.288C79.1787 276.085 79.5093 275.824 79.808 275.504L80.8 276.672C80.3733 277.163 79.8827 277.541 79.328 277.808C78.784 278.064 78.176 278.192 77.504 278.192ZM85.6028 278.192C84.9521 278.192 84.3441 278.032 83.7788 277.712C83.2134 277.381 82.7548 276.912 82.4028 276.304C82.0508 275.685 81.8748 274.944 81.8748 274.08C81.8748 273.205 82.0508 272.464 82.4028 271.856C82.7548 271.237 83.2134 270.768 83.7788 270.448C84.3441 270.117 84.9521 269.952 85.6028 269.952C86.2641 269.952 86.8774 270.117 87.4428 270.448C88.0081 270.768 88.4668 271.237 88.8188 271.856C89.1708 272.464 89.3468 273.205 89.3468 274.08C89.3468 274.944 89.1708 275.685 88.8188 276.304C88.4668 276.912 88.0081 277.381 87.4428 277.712C86.8774 278.032 86.2641 278.192 85.6028 278.192ZM85.6028 276.688C86.1788 276.688 86.6321 276.448 86.9628 275.968C87.2934 275.488 87.4588 274.859 87.4588 274.08C87.4588 273.291 87.2934 272.656 86.9628 272.176C86.6321 271.696 86.1788 271.456 85.6028 271.456C85.0268 271.456 84.5734 271.696 84.2428 272.176C83.9228 272.656 83.7628 273.291 83.7628 274.08C83.7628 274.859 83.9228 275.488 84.2428 275.968C84.5734 276.448 85.0268 276.688 85.6028 276.688ZM91.168 278V270.144H92.688L92.816 271.2H92.88C93.232 270.859 93.616 270.565 94.032 270.32C94.448 270.075 94.9227 269.952 95.456 269.952C96.2987 269.952 96.912 270.224 97.296 270.768C97.68 271.312 97.872 272.08 97.872 273.072V278H96.032V273.312C96.032 272.661 95.936 272.203 95.744 271.936C95.552 271.669 95.2373 271.536 94.8 271.536C94.4587 271.536 94.1547 271.621 93.888 271.792C93.632 271.952 93.3387 272.192 93.008 272.512V278H91.168ZM102.889 278.192C101.982 278.192 101.337 277.931 100.953 277.408C100.58 276.875 100.393 276.187 100.393 275.344V271.6H99.2731V270.224L100.489 270.144L100.713 268H102.249V270.144H104.249V271.6H102.249V275.344C102.249 276.261 102.617 276.72 103.353 276.72C103.492 276.72 103.636 276.704 103.785 276.672C103.934 276.629 104.062 276.587 104.169 276.544L104.489 277.904C104.276 277.979 104.03 278.043 103.753 278.096C103.486 278.16 103.198 278.192 102.889 278.192ZM107.463 278.192C106.78 278.192 106.22 277.979 105.783 277.552C105.356 277.125 105.143 276.571 105.143 275.888C105.143 275.045 105.511 274.395 106.247 273.936C106.983 273.467 108.156 273.147 109.767 272.976C109.756 272.56 109.644 272.203 109.431 271.904C109.228 271.595 108.86 271.44 108.327 271.44C107.943 271.44 107.564 271.515 107.191 271.664C106.828 271.813 106.471 271.995 106.119 272.208L105.447 270.976C105.884 270.699 106.375 270.459 106.919 270.256C107.473 270.053 108.06 269.952 108.679 269.952C109.66 269.952 110.391 270.245 110.871 270.832C111.361 271.408 111.607 272.245 111.607 273.344V278H110.087L109.959 277.136H109.895C109.543 277.435 109.164 277.685 108.759 277.888C108.364 278.091 107.932 278.192 107.463 278.192ZM108.055 276.752C108.375 276.752 108.663 276.677 108.919 276.528C109.185 276.368 109.468 276.155 109.767 275.888V274.128C108.7 274.267 107.959 274.475 107.543 274.752C107.127 275.019 106.919 275.349 106.919 275.744C106.919 276.096 107.025 276.352 107.239 276.512C107.452 276.672 107.724 276.752 108.055 276.752ZM113.809 278V270.144H115.649V278H113.809ZM114.737 268.768C114.406 268.768 114.139 268.672 113.937 268.48C113.734 268.288 113.633 268.032 113.633 267.712C113.633 267.403 113.734 267.152 113.937 266.96C114.139 266.768 114.406 266.672 114.737 266.672C115.067 266.672 115.334 266.768 115.537 266.96C115.739 267.152 115.841 267.403 115.841 267.712C115.841 268.032 115.739 268.288 115.537 268.48C115.334 268.672 115.067 268.768 114.737 268.768ZM117.996 278V270.144H119.516L119.644 271.2H119.708C120.06 270.859 120.444 270.565 120.86 270.32C121.276 270.075 121.751 269.952 122.284 269.952C123.127 269.952 123.74 270.224 124.124 270.768C124.508 271.312 124.7 272.08 124.7 273.072V278H122.86V273.312C122.86 272.661 122.764 272.203 122.572 271.936C122.38 271.669 122.065 271.536 121.628 271.536C121.287 271.536 120.983 271.621 120.716 271.792C120.46 271.952 120.167 272.192 119.836 272.512V278H117.996ZM130.309 278.192C129.584 278.192 128.928 278.032 128.341 277.712C127.755 277.381 127.291 276.912 126.949 276.304C126.608 275.685 126.437 274.944 126.437 274.08C126.437 273.227 126.608 272.491 126.949 271.872C127.301 271.253 127.755 270.779 128.309 270.448C128.864 270.117 129.445 269.952 130.053 269.952C130.768 269.952 131.365 270.112 131.845 270.432C132.325 270.741 132.688 271.179 132.933 271.744C133.179 272.299 133.301 272.944 133.301 273.68C133.301 274.064 133.275 274.363 133.221 274.576H128.229C128.315 275.269 128.565 275.808 128.981 276.192C129.397 276.576 129.92 276.768 130.549 276.768C130.891 276.768 131.205 276.72 131.493 276.624C131.792 276.517 132.085 276.373 132.373 276.192L132.997 277.344C132.624 277.589 132.208 277.792 131.749 277.952C131.291 278.112 130.811 278.192 130.309 278.192ZM128.213 273.328H131.701C131.701 272.72 131.568 272.245 131.301 271.904C131.035 271.552 130.635 271.376 130.101 271.376C129.643 271.376 129.232 271.547 128.869 271.888C128.517 272.219 128.299 272.699 128.213 273.328ZM135.059 278V270.144H136.579L136.707 271.536H136.771C137.048 271.024 137.384 270.635 137.779 270.368C138.173 270.091 138.579 269.952 138.995 269.952C139.368 269.952 139.667 270.005 139.891 270.112L139.571 271.712C139.432 271.669 139.304 271.637 139.187 271.616C139.069 271.595 138.925 271.584 138.755 271.584C138.445 271.584 138.12 271.707 137.779 271.952C137.437 272.187 137.144 272.603 136.899 273.2V278H135.059ZM54.6653 299V297.928C55.6147 297.011 56.4307 296.195 57.1133 295.48C57.8067 294.755 58.34 294.099 58.7133 293.512C59.0867 292.915 59.2733 292.36 59.2733 291.848C59.2733 291.315 59.1293 290.888 58.8413 290.568C58.5533 290.248 58.1373 290.088 57.5933 290.088C57.1987 290.088 56.836 290.2 56.5053 290.424C56.1853 290.648 55.8867 290.915 55.6093 291.224L54.5853 290.2C55.0547 289.699 55.54 289.315 56.0413 289.048C56.5427 288.771 57.14 288.632 57.8333 288.632C58.8147 288.632 59.5933 288.915 60.1693 289.48C60.756 290.045 61.0493 290.803 61.0493 291.752C61.0493 292.349 60.8787 292.963 60.5373 293.592C60.2067 294.211 59.7587 294.845 59.1933 295.496C58.6387 296.147 58.0253 296.829 57.3533 297.544C57.62 297.523 57.908 297.501 58.2173 297.48C58.5267 297.448 58.804 297.432 59.0493 297.432H61.5453V299H54.6653ZM64.4685 299.192C64.1378 299.192 63.8551 299.075 63.6205 298.84C63.3858 298.605 63.2685 298.307 63.2685 297.944C63.2685 297.581 63.3858 297.283 63.6205 297.048C63.8551 296.813 64.1378 296.696 64.4685 296.696C64.8098 296.696 65.0925 296.813 65.3165 297.048C65.5405 297.283 65.6525 297.581 65.6525 297.944C65.6525 298.307 65.5405 298.605 65.3165 298.84C65.0925 299.075 64.8098 299.192 64.4685 299.192ZM73.328 299V288.536H75.184V299H73.328ZM80.9636 299.192C80.249 299.192 79.6036 299.032 79.0276 298.712C78.4623 298.381 78.009 297.912 77.6676 297.304C77.337 296.685 77.1716 295.944 77.1716 295.08C77.1716 294.205 77.353 293.464 77.7156 292.856C78.0783 292.237 78.5583 291.768 79.1556 291.448C79.7636 291.117 80.4143 290.952 81.1076 290.952C81.609 290.952 82.0463 291.037 82.4196 291.208C82.793 291.379 83.1236 291.587 83.4116 291.832L82.5156 293.032C82.313 292.851 82.105 292.712 81.8916 292.616C81.6783 292.509 81.449 292.456 81.2036 292.456C80.5743 292.456 80.057 292.696 79.6516 293.176C79.257 293.656 79.0596 294.291 79.0596 295.08C79.0596 295.859 79.2516 296.488 79.6356 296.968C80.0303 297.448 80.537 297.688 81.1556 297.688C81.465 297.688 81.753 297.624 82.0196 297.496C82.297 297.357 82.5476 297.197 82.7716 297.016L83.5236 298.232C83.161 298.552 82.7556 298.792 82.3076 298.952C81.8596 299.112 81.4116 299.192 80.9636 299.192ZM87.8996 299.192C87.249 299.192 86.641 299.032 86.0756 298.712C85.5103 298.381 85.0516 297.912 84.6996 297.304C84.3476 296.685 84.1716 295.944 84.1716 295.08C84.1716 294.205 84.3476 293.464 84.6996 292.856C85.0516 292.237 85.5103 291.768 86.0756 291.448C86.641 291.117 87.249 290.952 87.8996 290.952C88.561 290.952 89.1743 291.117 89.7396 291.448C90.305 291.768 90.7636 292.237 91.1156 292.856C91.4676 293.464 91.6436 294.205 91.6436 295.08C91.6436 295.944 91.4676 296.685 91.1156 297.304C90.7636 297.912 90.305 298.381 89.7396 298.712C89.1743 299.032 88.561 299.192 87.8996 299.192ZM87.8996 297.688C88.4756 297.688 88.929 297.448 89.2596 296.968C89.5903 296.488 89.7556 295.859 89.7556 295.08C89.7556 294.291 89.5903 293.656 89.2596 293.176C88.929 292.696 88.4756 292.456 87.8996 292.456C87.3236 292.456 86.8703 292.696 86.5396 293.176C86.2196 293.656 86.0596 294.291 86.0596 295.08C86.0596 295.859 86.2196 296.488 86.5396 296.968C86.8703 297.448 87.3236 297.688 87.8996 297.688ZM93.4649 299V291.144H94.9849L95.1129 292.2H95.1769C95.5289 291.859 95.9129 291.565 96.3289 291.32C96.7449 291.075 97.2195 290.952 97.7529 290.952C98.5955 290.952 99.2089 291.224 99.5929 291.768C99.9769 292.312 100.169 293.08 100.169 294.072V299H98.3289V294.312C98.3289 293.661 98.2329 293.203 98.0409 292.936C97.8489 292.669 97.5342 292.536 97.0969 292.536C96.7555 292.536 96.4515 292.621 96.1849 292.792C95.9289 292.952 95.6355 293.192 95.3049 293.512V299H93.4649ZM57.8973 320.192C57.076 320.192 56.3827 320.053 55.8173 319.776C55.2627 319.488 54.804 319.147 54.4413 318.752L55.3213 317.568C55.6307 317.877 55.9827 318.144 56.3773 318.368C56.772 318.581 57.2253 318.688 57.7373 318.688C58.292 318.688 58.74 318.555 59.0813 318.288C59.4333 318.011 59.6093 317.632 59.6093 317.152C59.6093 316.8 59.5187 316.496 59.3373 316.24C59.1667 315.973 58.8627 315.771 58.4253 315.632C57.9987 315.493 57.396 315.424 56.6173 315.424V314.08C57.62 314.08 58.3133 313.925 58.6973 313.616C59.0813 313.307 59.2733 312.917 59.2733 312.448C59.2733 312.021 59.14 311.691 58.8733 311.456C58.6067 311.211 58.2387 311.088 57.7693 311.088C57.3747 311.088 57.012 311.179 56.6813 311.36C56.3507 311.531 56.0307 311.76 55.7213 312.048L54.7773 310.912C55.2147 310.528 55.6787 310.219 56.1693 309.984C56.6707 309.749 57.2253 309.632 57.8333 309.632C58.8147 309.632 59.6093 309.867 60.2173 310.336C60.8253 310.795 61.1293 311.456 61.1293 312.32C61.1293 312.885 60.9693 313.365 60.6493 313.76C60.34 314.144 59.908 314.443 59.3533 314.656V314.72C59.9507 314.88 60.452 315.173 60.8573 315.6C61.2627 316.027 61.4653 316.576 61.4653 317.248C61.4653 317.867 61.3 318.395 60.9693 318.832C60.6493 319.269 60.2173 319.605 59.6733 319.84C59.14 320.075 58.548 320.192 57.8973 320.192ZM64.4685 320.192C64.1378 320.192 63.8551 320.075 63.6205 319.84C63.3858 319.605 63.2685 319.307 63.2685 318.944C63.2685 318.581 63.3858 318.283 63.6205 318.048C63.8551 317.813 64.1378 317.696 64.4685 317.696C64.8098 317.696 65.0925 317.813 65.3165 318.048C65.5405 318.283 65.6525 318.581 65.6525 318.944C65.6525 319.307 65.5405 319.605 65.3165 319.84C65.0925 320.075 64.8098 320.192 64.4685 320.192ZM73.328 320V309.536H75.184V318.432H79.536V320H73.328ZM83.1033 320.192C82.4206 320.192 81.8606 319.979 81.4233 319.552C80.9966 319.125 80.7833 318.571 80.7833 317.888C80.7833 317.045 81.1513 316.395 81.8873 315.936C82.6233 315.467 83.7966 315.147 85.4073 314.976C85.3966 314.56 85.2846 314.203 85.0713 313.904C84.8686 313.595 84.5006 313.44 83.9673 313.44C83.5833 313.44 83.2046 313.515 82.8313 313.664C82.4686 313.813 82.1113 313.995 81.7593 314.208L81.0872 312.976C81.5246 312.699 82.0153 312.459 82.5593 312.256C83.1139 312.053 83.7006 311.952 84.3193 311.952C85.3006 311.952 86.0313 312.245 86.5113 312.832C87.0019 313.408 87.2473 314.245 87.2473 315.344V320H85.7273L85.5993 319.136H85.5353C85.1833 319.435 84.8046 319.685 84.3993 319.888C84.0046 320.091 83.5726 320.192 83.1033 320.192ZM83.6953 318.752C84.0153 318.752 84.3033 318.677 84.5593 318.528C84.8259 318.368 85.1086 318.155 85.4073 317.888V316.128C84.3406 316.267 83.5993 316.475 83.1833 316.752C82.7673 317.019 82.5593 317.349 82.5593 317.744C82.5593 318.096 82.6659 318.352 82.8793 318.512C83.0926 318.672 83.3646 318.752 83.6953 318.752ZM93.2573 320.192C92.5106 320.192 91.7959 319.851 91.1133 319.168H91.0653L90.9053 320H89.4493V308.704H91.2893V311.616L91.2413 312.928C91.5719 312.64 91.9346 312.405 92.3293 312.224C92.7239 312.043 93.1186 311.952 93.5133 311.952C94.4946 311.952 95.2573 312.315 95.8013 313.04C96.3453 313.765 96.6173 314.731 96.6173 315.936C96.6173 316.832 96.4573 317.6 96.1373 318.24C95.8173 318.869 95.4013 319.355 94.8893 319.696C94.3879 320.027 93.8439 320.192 93.2573 320.192ZM92.8733 318.672C93.3959 318.672 93.8333 318.443 94.1853 317.984C94.5373 317.525 94.7133 316.853 94.7133 315.968C94.7133 315.179 94.5799 314.565 94.3133 314.128C94.0466 313.691 93.6146 313.472 93.0173 313.472C92.4626 313.472 91.8866 313.765 91.2893 314.352V318.016C91.5666 318.251 91.8386 318.421 92.1053 318.528C92.3826 318.624 92.6386 318.672 92.8733 318.672ZM101.825 320.192C101.1 320.192 100.444 320.032 99.8569 319.712C99.2702 319.381 98.8062 318.912 98.4649 318.304C98.1235 317.685 97.9529 316.944 97.9529 316.08C97.9529 315.227 98.1235 314.491 98.4649 313.872C98.8169 313.253 99.2702 312.779 99.8249 312.448C100.38 312.117 100.961 311.952 101.569 311.952C102.284 311.952 102.881 312.112 103.361 312.432C103.841 312.741 104.204 313.179 104.449 313.744C104.694 314.299 104.817 314.944 104.817 315.68C104.817 316.064 104.79 316.363 104.737 316.576H99.7449C99.8302 317.269 100.081 317.808 100.497 318.192C100.913 318.576 101.436 318.768 102.065 318.768C102.406 318.768 102.721 318.72 103.009 318.624C103.308 318.517 103.601 318.373 103.889 318.192L104.513 319.344C104.14 319.589 103.724 319.792 103.265 319.952C102.806 320.112 102.326 320.192 101.825 320.192ZM99.7289 315.328H103.217C103.217 314.72 103.084 314.245 102.817 313.904C102.55 313.552 102.15 313.376 101.617 313.376C101.158 313.376 100.748 313.547 100.385 313.888C100.033 314.219 99.8142 314.699 99.7289 315.328ZM108.366 320.192C107.716 320.192 107.252 319.995 106.974 319.6C106.708 319.205 106.574 318.667 106.574 317.984V308.704H108.414V318.08C108.414 318.304 108.457 318.464 108.542 318.56C108.628 318.645 108.718 318.688 108.814 318.688C108.857 318.688 108.894 318.688 108.926 318.688C108.969 318.677 109.028 318.667 109.102 318.656L109.342 320.032C109.236 320.075 109.097 320.112 108.926 320.144C108.766 320.176 108.58 320.192 108.366 320.192Z" fill="var(--button-anatomy-body-color)"/> + </g> + </g> + </g> + </g> +</svg> diff --git a/packages/storybook/src/nimble/dialog/dialog.mdx b/packages/storybook/src/nimble/dialog/dialog.mdx index 4e7292b100..d0e12ded4b 100644 --- a/packages/storybook/src/nimble/dialog/dialog.mdx +++ b/packages/storybook/src/nimble/dialog/dialog.mdx @@ -80,7 +80,7 @@ MyDialogOwner.razor MyDialogOwner.razor.cs -```cs +```text private DialogData _model = new(); private NimbleDialog! _dialog; diff --git a/packages/storybook/src/nimble/patterns/button/button-styling-docs.mdx b/packages/storybook/src/nimble/patterns/button/button-styling-docs.mdx index d4430055ef..65e86be6fd 100644 --- a/packages/storybook/src/nimble/patterns/button/button-styling-docs.mdx +++ b/packages/storybook/src/nimble/patterns/button/button-styling-docs.mdx @@ -1,112 +1,115 @@ import { NimbleIconKey } from '@ni/nimble-react/icons/key'; -import { Frame, Container, Column, Do, Dont, Divider } from '../../../utilities/story-layout'; +import { StoryShowcase } from '../../../utilities/story-layout'; +import { buttonSections } from '../../button/button-sections'; -### Appearances +<h3 id={buttonSections.appearances.id}>{buttonSections.appearances.title}</h3> -These are the standard styling options for the button. Each should be considered -for use before employing an appearance variant. +These are the standard styling options for the button, ordered from least to +most visually prominent. Consider each option before employing an appearance +variant. #### Ghost Button -<Frame> - <Container> - <Column stylingClass="controls"> +<StoryShowcase> + <div className="story-showcase-preview"> + <div className="story-showcase-actions"> <Button appearance="ghost">Ghost Button</Button> <Button appearance="ghost" content-hidden> <NimbleIconKey slot="start"></NimbleIconKey> Ghost Button </Button> - - </Column> - <Column> - <ul> - <li>Ghost is the default appearance and should be the first considered for use.</li> - <li> - Use as the default and standard option to create a clean airy and open UI - feel. Ghost buttons fit comfortably in tight spaces and help control the - visual density of the UI. - </li> - <li> - Be careful when using that the surrounding context does not cause this - button to be confused for emphasized body text, tabs or a standalone links. - </li> - <li>Use in combination with a primary outline or primary block buttons to create a hierarchy of importance. There is no primary ghost button.</li> - </ul> - </Column> - </Container> -</Frame> + </div> + </div> + <div className="story-showcase-description"> + <h5>When to Use</h5> + <ul> + <li>Ghost is the default appearance and should be the first considered for use.</li> + <li> + Use as the default and standard option to create a clean airy and open UI + feel. Ghost buttons fit comfortably in tight spaces and help control the + visual density of the UI. + </li> + <li> + Be careful when using that the surrounding context does not cause this + button to be confused for emphasized body text, tabs or a standalone links. + </li> + <li>Use in combination with a primary outline or primary block buttons to create a hierarchy of importance. There is no primary ghost button.</li> + </ul> + </div> +</StoryShowcase> #### Outline Button -<Frame> - <Container> - <Column stylingClass="controls"> +<StoryShowcase> + <div className="story-showcase-preview"> + <div className="story-showcase-actions"> <Button appearance="outline">Outline Button</Button> <Button appearance="outline" content-hidden> <NimbleIconKey slot="start"></NimbleIconKey> Outline Button </Button> - </Column> - <Column> - <ul> - <li> - Outline is the secondary style and should be considered for - use when ghost button is not sufficient. - </li> - <li> - Use as an alternative standard button when a ghost button is - not suitable. Use like a ghost button to create a clean, - light and airy feel. - </li> - <li> - The outline button is more visually direct about the - control's functionality than a ghost button. - </li> - <li> - Use in combination with ghost buttons (but not block - buttons) to create hierarchy. - </li> - </ul> - </Column> - </Container> -</Frame> + </div> + </div> + <div className="story-showcase-description"> + <h5>When to Use</h5> + <ul> + <li> + Outline is the secondary style and should be considered for + use when ghost button is not sufficient. + </li> + <li> + Use as an alternative standard button when a ghost button is + not suitable. Use like a ghost button to create a clean, + light and airy feel. + </li> + <li> + The outline button is more visually direct about the + control's functionality than a ghost button. + </li> + <li> + Use in combination with ghost buttons (but not block + buttons) to create hierarchy. + </li> + </ul> + </div> +</StoryShowcase> #### Block Button -<Frame> - <Container> - <Column stylingClass="controls"> +<StoryShowcase> + <div className="story-showcase-preview"> + <div className="story-showcase-actions"> <Button appearance="block">Block Button</Button> <Button appearance="block" content-hidden> <NimbleIconKey slot="start"></NimbleIconKey> Block Button </Button> - </Column> - <Column> - <ul> - <li> - Block is the tertiary style used for creating the most eye - catching and functionally direct button. Use in areas where - controls are not often present or obvious, or when lots of - busy information can cause an important control to be - overlooked. - </li> - <li> - Use as a standard button when the most visible solution is - required. Use as an alternative to overly subtle button - solutions when it is important to emphasize an action and - the functionality of the control. - </li> - <li> - Use in combination with ghost buttons (but not outline - buttons) to create hierarchy. - </li> - </ul> - </Column> - </Container> -</Frame> - -### Appearance Variants + </div> + </div> + <div className="story-showcase-description"> + <h5>When to Use</h5> + <ul> + <li> + Block is the tertiary style used for creating the most eye + catching and functionally direct button. Use in areas where + controls are not often present or obvious, or when lots of + busy information can cause an important control to be overlooked. + </li> + <li> + Use as a standard button when the most visible solution is + required. Use as an alternative to overly subtle button + solutions when it is important to emphasize an action and + the functionality of the control. + </li> + <li> + Use in combination with ghost buttons (but not outline + buttons) to create hierarchy. + </li> + </ul> + </div> +</StoryShowcase> + +<h3 id={buttonSections.appearanceVariants.id}>{buttonSections.appearanceVariants.title}</h3> Button appearance variants are mainly used when a button needs be distinguished for one of the following reasons: @@ -119,75 +122,76 @@ There are two available values for `appearance-variant`: `primary` and `accent`. #### Accent Button -<Frame> - <Container config="325px 1fr"> - <Column stylingClass="controls"> - <Button appearance="ghost">Ghost</Button> - <Button appearance="ghost">Ghost</Button> - <Button - appearance="outline" - appearance-variant="accent" - >Outline Accent</Button> - <Divider /> - <Button appearance="ghost">Ghost</Button> - <Button appearance="ghost">Ghost</Button> - <Button - appearance="block" - appearance-variant="accent" - >Block Accent</Button> - </Column> - <Column> - <Do> +<StoryShowcase> + <div className="story-showcase-preview"> + <div className="story-showcase-button-stack"> + <div className="story-showcase-button-row"> + <Button appearance="ghost">Ghost</Button> + <Button appearance="ghost">Ghost</Button> + <Button appearance="outline" appearance-variant="accent">Outline Accent</Button> + </div> + <hr/> + <div className="story-showcase-button-row"> + <Button appearance="ghost">Ghost</Button> + <Button appearance="ghost">Ghost</Button> + <Button appearance="block" appearance-variant="accent">Block Accent</Button> + </div> + </div> + </div> + <div className="story-showcase-description"> + <h5>When to Use</h5> + <ul> + <li> Use only 1 accent button in a section. It should be used when trying to achieve the most prominent eye-catching approach. Consider the contextual implications of using a green colored button in relation to its name or metaphor. Remember that color has accessibility constraints and the color alone should not be relied on. - </Do> - <Do> - Use in situations that lack color and enthusiasm to help support - the brand. - </Do> - <Do>Use in combination with ghost buttons to create hierarchy.</Do> - <Dont> + </li> + <li>Use in situations that lack color and enthusiasm to help support the brand.</li> + <li>Use in combination with ghost buttons to create hierarchy.</li> + <li> Do not use an outline button with a block button to create hierarchy. - </Dont> - </Column> - </Container> -</Frame> + </li> + </ul> + </div> +</StoryShowcase> #### Primary Button -<Frame> - <Container config="325px 1fr"> - <Column stylingClass="controls"> - <Button appearance="ghost">Ghost</Button> - <Button appearance="ghost">Ghost</Button> - <Button appearance="outline" appearance-variant="primary"> - Outline Primary - </Button> - <Divider/> - <Button appearance="ghost">Ghost</Button> - <Button appearance="ghost">Ghost</Button> - <Button appearance="block" appearance-variant="primary"> - Block Primary - </Button> - </Column> - <Column> - <Do> +<StoryShowcase> + <div className="story-showcase-preview"> + <div className="story-showcase-button-stack"> + <div className="story-showcase-button-row"> + <Button appearance="ghost">Ghost</Button> + <Button appearance="ghost">Ghost</Button> + <Button appearance="outline" appearance-variant="primary">Outline Primary</Button> + </div> + <hr/> + <div className="story-showcase-button-row"> + <Button appearance="ghost">Ghost</Button> + <Button appearance="ghost">Ghost</Button> + <Button appearance="block" appearance-variant="primary">Block Primary</Button> + </div> + </div> + </div> + <div className="story-showcase-description"> + <h5>When to Use</h5> + <ul> + <li> Use only 1 primary button in a section. It should be used when there is a conflict with color and its context. - </Do> - <Do>Use in combination with ghost buttons to create hierarchy.</Do> - <Dont> + </li> + <li>Use in combination with ghost buttons to create hierarchy.</li> + <li> Do not use an outline button with a block button to create hierarchy. - </Dont> - </Column> - </Container> -</Frame> + </li> + </ul> + </div> +</StoryShowcase> #### Examples diff --git a/packages/storybook/src/nimble/select/select.mdx b/packages/storybook/src/nimble/select/select.mdx index 0d5f0b8c13..f529f0e641 100644 --- a/packages/storybook/src/nimble/select/select.mdx +++ b/packages/storybook/src/nimble/select/select.mdx @@ -13,11 +13,41 @@ import { comboboxTag } from '@ni/nimble-components/dist/esm/combobox'; import { listOptionTag } from '@ni/nimble-components/dist/esm/list-option'; import { listOptionGroupTag } from '@ni/nimble-components/dist/esm/list-option-group'; import { radioTag } from '@ni/nimble-components/dist/esm/radio'; -import { Tag } from '../../utilities/story-layout'; +import { + StoryApi, + StoryPage, + StoryPreview, + StorySection, + Tag +} from '../../utilities/story-layout'; + +export const sections = { + overview: { id: 'overview', title: 'Overview', level: 2 }, + implementationApi: { id: 'implementation-api', title: 'Implementation & API', level: 2 }, + api: { id: 'api', title: 'API', level: 3 }, + listOption: { id: 'list-option', title: 'List Option', level: 3 }, + listOptionGroup: { id: 'list-option-group', title: 'List Option Group', level: 3 }, + styling: { id: 'styling', title: 'Styling', level: 2 }, + examples: { id: 'examples', title: 'Examples', level: 2 }, + placeholder: { id: 'placeholder', title: 'Placeholder', level: 3 }, + dynamicOptions: { id: 'dynamic-options', title: 'Dynamic options', level: 3 } +}; <Meta of={selectStories} /> +<StoryPage toc={[ + sections.overview, + sections.implementationApi, + sections.api, + sections.listOption, + sections.listOptionGroup, + sections.styling, + sections.examples, + sections.placeholder, + sections.dynamicOptions +]}> <Title of={selectStories} /> +<StorySection {...sections.overview}> The Select control allows users to choose from a list of options displayed in a dropdown menu. @@ -25,15 +55,20 @@ Use <Tag name={radioTag} /> when you need to present a small set of options. Use <Tag name={comboboxTag} /> if users should be able to enter a custom value in addition to selecting from the list. +</StorySection> -<Canvas of={selectStories.select} /> +<StorySection {...sections.implementationApi}> +<StoryPreview> + <Canvas of={selectStories.select} /> +</StoryPreview> -## API +<StoryApi> +<h3 id={sections.api.id}>{sections.api.title}</h3> <Controls of={selectStories.select} /> <ComponentApisLink /> -### List Option +<h3 id={sections.listOption.id}>{sections.listOption.title}</h3> <Description of={listOptionStories} /> <Controls of={listOptionStories.listOption} /> @@ -48,22 +83,27 @@ on the Select control. Without this, the Select may display unexpected behavior (like reverting to the placeholder value when updating the list of filtered options). -### List Option Group +<h3 id={sections.listOptionGroup.id}>{sections.listOptionGroup.title}</h3> <Description of={listOptionGroupStories} /> <Controls of={listOptionGroupStories.listOptionGroup} /> -## Styling +</StoryApi> +</StorySection> + +<StorySection {...sections.styling}> By default, the select control resizes to the selected option size. In most layouts, give the select a specific `width` to align with other items. Ensure the control is wide enough so labels do not wrap. +</StorySection> + {/* ## Usage */} -## Examples +<StorySection {...sections.examples}> -### Placeholder +<h3 id={sections.placeholder.id}>{sections.placeholder.title}</h3> To display placeholder text within the select you must provide an option that has the `disabled`, `selected` and `hidden` attributes set. This option will not @@ -81,9 +121,11 @@ the end of the prompt. placeholder as `disabled` and `hidden` and ensure that it is the first option in the list. See [#2783](https://github.com/ni/nimble/issues/2783) for more detail.* -<Canvas of={selectStories.placeholder} /> +<StoryPreview> + <Canvas of={selectStories.placeholder} /> +</StoryPreview> -### Dynamic options +<h3 id={sections.dynamicOptions.id}>{sections.dynamicOptions.title}</h3> There are scenarios that require not all possible options to be available initially, such as when the set of all options is too large to be loaded at @@ -125,6 +167,9 @@ group should be displayed, and all options within the group should be displayed as well. If the group label does not match the filter text, then options within the group should only be displayed if they match the filter text. +</StorySection> +</StoryPage> + {/* ## Accessibility */} {/* ## Resources */} diff --git a/packages/storybook/src/nimble/table/table.mdx b/packages/storybook/src/nimble/table/table.mdx index 84bd087fae..d2e6cb3a29 100644 --- a/packages/storybook/src/nimble/table/table.mdx +++ b/packages/storybook/src/nimble/table/table.mdx @@ -70,7 +70,7 @@ MyComponent.razor MyComponent.razor.cs -```cs +```text ... private NimbleTable<MyRecordType>? _table; private IEnumerable<MyRecordType> TableData { get; set; } = Enumerable.Empty<MyRecordType>(); diff --git a/packages/storybook/src/ok/ts/icon-dynamic/ts-icon-dynamic.mdx b/packages/storybook/src/ok/ts/icon-dynamic/ts-icon-dynamic.mdx index 85512f456c..f99bd9c176 100644 --- a/packages/storybook/src/ok/ts/icon-dynamic/ts-icon-dynamic.mdx +++ b/packages/storybook/src/ok/ts/icon-dynamic/ts-icon-dynamic.mdx @@ -41,7 +41,7 @@ customElements Register once after first render using the `OkTsIconDynamic.RegisterIconDynamicAsync` helper: -```cs +```text @inject IJSRuntime JSRuntime protected override async Task OnAfterRenderAsync(bool firstRender) diff --git a/packages/storybook/src/utilities/story-layout.tsx b/packages/storybook/src/utilities/story-layout.tsx index 4ae12100fa..ac9e0e1558 100644 --- a/packages/storybook/src/utilities/story-layout.tsx +++ b/packages/storybook/src/utilities/story-layout.tsx @@ -1,7 +1,43 @@ import React, { type ReactNode } from 'react'; import { NimbleIconCheck } from '@ni/nimble-react/icons/check'; -import { NimbleThemeProvider } from '@ni/nimble-react/theme-provider'; -import { NimbleIconExclamationMark } from '@ni/nimble-react/icons/exclamation-mark'; +import { NimbleThemeProvider, type ThemeProvider } from '@ni/nimble-react/theme-provider'; +import { NimbleIconXmark } from '@ni/nimble-react/icons/xmark'; +import { + applicationBackgroundColor, + bodyEmphasizedFont, + bodyEmphasizedFontColor, + bodyDisabledFontColor, + bodyFontColor, + bodyPlus1FontFamily, + bodyPlus1FontSize, + bodyPlus1FontWeight, + bodyPlus1EmphasizedFontWeight, + bodyPlus1EmphasizedFont, + bodyPlus1Font, + bodyPlus1FontColor, + buttonAccentBlockFontColor, + buttonLabelFont, + buttonLabelFontWeight, + cardBorderColor, + dialogLargeWidth, + dividerBackgroundColor, + dividerWidth, + elevation1BoxShadow, + failColor, + largePadding, + linkProminentFontColor, + mediumPadding, + passColor, + smallPadding, + standardPadding, + subtitlePlus1FontColor, + tableRowBorderColor, + tooltipCaptionFont, + titlePlus1FontColor, + titlePlus2FontColor, + headlinePlus1FontColor, + iconColor, +} from '@ni/nimble-components/dist/esm/theme-provider/design-tokens'; export const css = (strings: TemplateStringsArray, ...values: unknown[]): React.JSX.Element => <style> {`@scope {${String.raw({ raw: strings }, ...values)}}`} @@ -11,28 +47,459 @@ interface ChildrenProp { children?: ReactNode; } -export const theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; +const getPreferredTheme = (): 'dark' | 'light' => ( + window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' +); -/** - * Renders a frame to match visual design of existing Storybook Doc blocks. - */ -export const Frame = ({ children }: ChildrenProp): React.JSX.Element => { - return <NimbleThemeProvider theme={theme}> - <div> +export const theme = getPreferredTheme(); + +interface StorySectionProps extends ChildrenProp { + id: string; + title: string; +} + +export interface StoryTocItem { + id: string; + title: string; + level: 2 | 3; +} + +interface StoryPageProps extends ChildrenProp { + toc?: readonly StoryTocItem[]; +} + +export const StoryTableOfContents = ({ items }: { items: readonly StoryTocItem[] }): React.JSX.Element => { + const [activeId, setActiveId] = React.useState(items[0]?.id ?? ''); + + React.useEffect(() => { + const headings = items + .map(item => document.getElementById(item.id)) + .filter((heading): heading is HTMLElement => heading instanceof HTMLElement); + + if (headings.length === 0) { + return undefined; + } + + const firstHeading = headings[0]; + if (!firstHeading) { + return undefined; + } + + const hashHeading = headings.find(heading => heading.id === window.location.hash.slice(1)); + if (hashHeading) { + hashHeading.scrollIntoView({ block: 'start' }); + setActiveId(hashHeading.id); + } + + const updateActiveHeading = (): void => { + if (window.scrollY === 0) { + setActiveId(firstHeading.id); + return; + } + let activeHeading = firstHeading; + const activationLine = window.innerHeight / 3; + for (const heading of headings.slice(1)) { + if (heading.getBoundingClientRect().top <= activationLine) { + activeHeading = heading; + } + } + setActiveId(activeHeading.id); + }; + + updateActiveHeading(); + window.addEventListener('scroll', updateActiveHeading, { passive: true }); + window.addEventListener('resize', updateActiveHeading); + return () => { + window.removeEventListener('scroll', updateActiveHeading); + window.removeEventListener('resize', updateActiveHeading); + }; + }, [items]); + + return <nav aria-labelledby='story-table-of-contents' className='story-table-of-contents'> + {css` + :scope { + align-self: start; + min-width: 0; + position: sticky; + top: var(${standardPadding.cssCustomProperty}); + font: var(${tooltipCaptionFont.cssCustomProperty}); + } + :scope h2 { + margin: 0 0 var(${standardPadding.cssCustomProperty}); + padding-inline-start: var(${mediumPadding.cssCustomProperty}); + color: var(${bodyDisabledFontColor.cssCustomProperty}); + font: var(${buttonLabelFont.cssCustomProperty}); + text-transform: uppercase; + } + :scope ol { + margin: 0; + padding: 0; + border-inline-start: var(${dividerWidth.cssCustomProperty}) solid var(${dividerBackgroundColor.cssCustomProperty}); + list-style: none; + } + :scope li { + position: relative; + margin: 0; + padding: var(${smallPadding.cssCustomProperty}) var(${mediumPadding.cssCustomProperty}); + } + :scope li[data-level='3'] { + padding-inline-start: var(${largePadding.cssCustomProperty}); + } + :scope li[data-active='true']::before { + position: absolute; + inset-block: 0; + inset-inline-start: calc(-1 * var(${dividerWidth.cssCustomProperty})); + border-inline-start: var(${dividerWidth.cssCustomProperty}) solid var(${linkProminentFontColor.cssCustomProperty}); + content: ''; + } + :scope a { + display: block; + color: var(${bodyDisabledFontColor.cssCustomProperty}); + font: inherit; + text-decoration: none; + } + :scope li[data-active='true'] a { + color: var(${bodyFontColor.cssCustomProperty}); + font-weight: var(${bodyPlus1EmphasizedFontWeight.cssCustomProperty}, 600); + } + :scope a:focus-visible { + outline: var(${dividerWidth.cssCustomProperty}) solid var(${linkProminentFontColor.cssCustomProperty}); + outline-offset: var(${smallPadding.cssCustomProperty}); + } + @media (max-width: 800px) { + :scope { + display: none; + } + } + `} + <h2 id='story-table-of-contents'>Contents</h2> + <ol> + {items.map(item => <li key={item.id} data-level={item.level} data-active={item.id === activeId}> + <a + href={`#${item.id}`} + aria-current={item.id === activeId ? 'location' : undefined} + onClick={() => setActiveId(item.id)} + >{item.title}</a> + </li>)} + </ol> + </nav>; +}; + +export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Element => { + const themeProviderRef = React.useRef<ThemeProvider>(null); + + React.useLayoutEffect(() => { + themeProviderRef.current?.setAttribute('theme', theme); + }, []); + + return <NimbleThemeProvider ref={themeProviderRef} theme={theme}> + <div className='story-page'> {css` :scope { - margin-bottom: 32px !important; - border: 1px solid rgba(128, 128, 128, 0.2); - border-radius: 4px; - box-shadow: 0px 1px 4px rgba(128, 128, 128, 0.22); - padding: 16px; + position: relative; + gap: var(${standardPadding.cssCustomProperty}); + width: 100%; + max-width: var(${dialogLargeWidth.cssCustomProperty}); + margin-inline: auto; + } + :scope > .story-page-toc { + position: absolute; + inset-block: 0; + inset-inline-start: calc(100% + var(${standardPadding.cssCustomProperty})); + width: max-content; + } + :scope > .story-page-content { + min-width: 0; + } + @media (max-width: 640px) { + :scope { + gap: 0; + } } `} - {children} + {toc.length > 0 ? <div className='story-page-toc'><StoryTableOfContents items={toc}/></div> : null} + <main className='story-page-content'> + {css` + :scope { + box-sizing: border-box; + width: 100%; + min-width: 0; + margin-inline: auto; + padding: 0 var(${standardPadding.cssCustomProperty}) var(${largePadding.cssCustomProperty}); + color: var(${bodyPlus1FontColor.cssCustomProperty}); + background: var(${applicationBackgroundColor.cssCustomProperty}); + font-family: var(${bodyPlus1FontFamily.cssCustomProperty}); + font-size: var(${bodyPlus1FontSize.cssCustomProperty}); + font-weight: var(${bodyPlus1FontWeight.cssCustomProperty}); + line-height: 21px; + } + :scope *, :scope *::before, :scope *::after { + box-sizing: border-box; + } + :scope h1 { + margin: 0 0 calc(var(${largePadding.cssCustomProperty}) + var(${standardPadding.cssCustomProperty})); + scroll-margin-block-start: var(${largePadding.cssCustomProperty}); + color: var(${headlinePlus1FontColor.cssCustomProperty}); + font-family: var(${bodyPlus1FontFamily.cssCustomProperty}); + font-size: 52px; + font-weight: var(${buttonLabelFontWeight.cssCustomProperty}, 600); + line-height: 56px; + letter-spacing: -1px; + } + :scope h2 { + margin: calc(var(${largePadding.cssCustomProperty}) + var(${standardPadding.cssCustomProperty})) 0 var(${largePadding.cssCustomProperty}); + scroll-margin-block-start: var(${largePadding.cssCustomProperty}); + padding-bottom: calc(var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})); + border-bottom: var(${dividerWidth.cssCustomProperty}) solid var(${dividerBackgroundColor.cssCustomProperty}); + color: var(${titlePlus2FontColor.cssCustomProperty}); + font-family: var(${bodyPlus1FontFamily.cssCustomProperty}); + font-size: 30px; + font-weight: var(${bodyPlus1FontWeight.cssCustomProperty}); + line-height: 34px; + } + :scope h3 { + margin: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})) 0 var(${standardPadding.cssCustomProperty}); + scroll-margin-block-start: var(${largePadding.cssCustomProperty}); + color: var(${subtitlePlus1FontColor.cssCustomProperty}); + font-family: var(${bodyPlus1FontFamily.cssCustomProperty}); + font-size: 21px; + font-weight: var(${buttonLabelFontWeight.cssCustomProperty}, 600); + line-height: 26px; + letter-spacing: -1px; + } + :scope h4 { + margin: var(${largePadding.cssCustomProperty}) 0 calc(var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})); + color: var(${bodyEmphasizedFontColor.cssCustomProperty}); + font-family: var(${bodyPlus1FontFamily.cssCustomProperty}); + font-size: var(${bodyPlus1FontSize.cssCustomProperty}); + font-weight: var(${buttonLabelFontWeight.cssCustomProperty}, 600); + line-height: 21px; + } + :scope h5 { + color: var(${bodyEmphasizedFontColor.cssCustomProperty}); + font-family: var(${bodyPlus1FontFamily.cssCustomProperty}); + font-size: 14px; + font-weight: var(${buttonLabelFontWeight.cssCustomProperty}, 600); + line-height: 16px; + } + :scope p, + :scope li { + font: var(${bodyPlus1Font.cssCustomProperty}); + line-height: 21px; + } + :scope a { + font: inherit; + } + :scope strong { + font-weight: var(${buttonLabelFontWeight.cssCustomProperty}, 600); + } + :scope p:first-child { + margin-top: 0; + } + :scope p { + max-width: 750px; + } + :scope code { + font: var(${bodyEmphasizedFont.cssCustomProperty}); + } + `} + {children} + </main> </div> </NimbleThemeProvider>; }; +export const StorySection = ({ id, title, children }: StorySectionProps): React.JSX.Element => { + return <section> + <h2 id={id}>{title}</h2> + {children} + </section>; +}; + +export const StoryPreview = ({ children }: ChildrenProp): React.JSX.Element => { + return <div> + {css` + :scope { + margin: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})) 0 calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty})); + border: var(${dividerWidth.cssCustomProperty}) solid var(${tableRowBorderColor.cssCustomProperty}); + border-radius: 4px; + overflow: hidden; + background: var(${applicationBackgroundColor.cssCustomProperty}); + } + :scope > * { + margin: 0 !important; + } + `} + {children} + </div>; +}; + +export const StoryApi = ({ children }: ChildrenProp): React.JSX.Element => { + return <div> + {css` + :scope { + margin: var(${largePadding.cssCustomProperty}) 0 calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty})); + overflow-x: auto; + } + :scope > * { + margin-top: var(${standardPadding.cssCustomProperty}); + } + :scope > *:first-child { + margin-top: 0; + } + `} + {children} + </div>; +}; + +interface StoryAnatomyProps extends ChildrenProp { + src?: string; + alt: string; +} + +export const StoryAnatomy = ({ children, src, alt }: StoryAnatomyProps): React.JSX.Element => { + return <figure> + {css` + :scope { + margin: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})) 0 calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty})); + border: var(${dividerWidth.cssCustomProperty}) solid var(${cardBorderColor.cssCustomProperty}); + border-radius: 4px; + overflow: hidden; + background: var(${applicationBackgroundColor.cssCustomProperty}); + } + :scope img { + display: block; + width: 100%; + height: auto; + } + `} + {children ?? (src ? <img src={src} alt={alt}/> : null)} + </figure>; +}; + +export const StoryGuidance = ({ children }: ChildrenProp): React.JSX.Element => { + return <div> + {css` + :scope { + margin-top: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})); + } + :scope > * + * { + margin-top: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})); + } + `} + {children} + </div>; +}; + +export const StoryGuidanceGrid = ({ children }: ChildrenProp): React.JSX.Element => { + return <div> + {css` + :scope { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: start; + gap: calc(var(${standardPadding.cssCustomProperty}) + var(${standardPadding.cssCustomProperty})); + margin-top: var(${largePadding.cssCustomProperty}); + } + @media (max-width: 640px) { + :scope { + grid-template-columns: 1fr; + gap: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})); + } + } + `} + {children} + </div>; +}; + +export const StoryShowcase = ({ children }: ChildrenProp): React.JSX.Element => { + return <div> + {css` + :scope { + margin: 0 0 var(${largePadding.cssCustomProperty}); + overflow: hidden; + border: var(${dividerWidth.cssCustomProperty}) solid var(${cardBorderColor.cssCustomProperty}); + border-radius: 4px; + background: var(${applicationBackgroundColor.cssCustomProperty}); + } + :scope .story-showcase-preview { + display: flex; + min-height: 148px; + align-items: center; + justify-content: center; + gap: var(${standardPadding.cssCustomProperty}); + padding: var(${largePadding.cssCustomProperty}); + background: var(${applicationBackgroundColor.cssCustomProperty}); + } + :scope .story-showcase-description { + padding: var(${standardPadding.cssCustomProperty}); + border-top: var(${dividerWidth.cssCustomProperty}) solid var(${cardBorderColor.cssCustomProperty}); + } + :scope .story-showcase-description > *:first-child { + margin-top: 0; + } + :scope .story-showcase-description > *:last-child { + margin-bottom: 0; + } + :scope .story-showcase-preview > * { + margin: 0 !important; + } + :scope .story-showcase-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: var(${standardPadding.cssCustomProperty}); + } + :scope .story-showcase-button-stack { + display: flex; + width: min(100%, 420px); + flex-direction: column; + align-items: center; + gap: var(${standardPadding.cssCustomProperty}); + } + :scope .story-showcase-button-row { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: var(${standardPadding.cssCustomProperty}); + } + :scope .story-showcase-button-stack hr { + width: 100%; + margin: 0; + border: 0; + border-top: var(${dividerWidth.cssCustomProperty}) solid var(${dividerBackgroundColor.cssCustomProperty}); + } + @media (max-width: 640px) { + :scope .story-showcase-preview { + min-height: 128px; + padding: var(${standardPadding.cssCustomProperty}); + } + } + `} + {children} + </div>; +}; + +/** + * Renders a frame to match visual design of existing Storybook Doc blocks. + */ +export const Frame = ({ children }: ChildrenProp): React.JSX.Element => { + return <div> + {css` + :scope { + margin-bottom: var(${largePadding.cssCustomProperty}) !important; + overflow: hidden; + border: var(${dividerWidth.cssCustomProperty}) solid var(${cardBorderColor.cssCustomProperty}); + border-radius: 4px; + box-shadow: var(${elevation1BoxShadow.cssCustomProperty}); + background: var(${applicationBackgroundColor.cssCustomProperty}); + } + `} + {children} + </div>; +}; + interface ContainerProp { children?: ReactNode; config?: string; @@ -47,7 +514,15 @@ export const Container = ({ children, config = '200px 1fr' }: ContainerProp): Re {css` :scope { display: grid; - align-items: center; + min-width: 0; + align-items: stretch; + gap: calc(var(${standardPadding.cssCustomProperty}) + var(${standardPadding.cssCustomProperty})); + } + @media (max-width: 640px) { + :scope { + grid-template-columns: 1fr !important; + gap: var(${standardPadding.cssCustomProperty}); + } } `} {children} @@ -66,12 +541,22 @@ interface ColumnProp { export const Column = ({ children, stylingClass = '' }: ColumnProp): React.JSX.Element => { return <div className={stylingClass}> {css` + :scope { + min-width: 0; + } :scope.controls { - padding-left: var(--ni-nimble-medium-padding); - padding-right: var(--ni-nimble-medium-padding); + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: var(${standardPadding.cssCustomProperty}); + padding: var(${largePadding.cssCustomProperty}); } - :scope > * { - margin: var(--ni-nimble-small-padding); + :scope.controls > * { + margin: 0; + } + :scope:not(.controls) > * { + margin: var(${smallPadding.cssCustomProperty}); } `} {children} @@ -82,35 +567,167 @@ export const Column = ({ children, stylingClass = '' }: ColumnProp): React.JSX.E * Renders a simple divider to vertically separate the contents of a <Column>. */ export const Divider = (): React.JSX.Element => { - return <hr/>; + return <div> + {css` + :scope hr { + width: 100%; + margin: var(${mediumPadding.cssCustomProperty}) 0; + border: 0; + border-top: var(${dividerWidth.cssCustomProperty}) solid var(${dividerBackgroundColor.cssCustomProperty}); + } + `} + <hr/> + </div>; }; -/** - * Renders a "Do" section for Storybook documentation. - */ -export const Do = ({ children }: ChildrenProp): React.JSX.Element => { - return ( - <Container config='48px 1fr'> - <Column> - <NimbleIconCheck style={{ width: '28px', height: '28px' }} severity='success'/> - </Column> - <Column><div>{children}</div></Column> - </Container> - ); +interface GuidanceProp extends ChildrenProp { + example?: ReactNode; + title: string; +} + +const Guidance = ( + { + children, + example, + title, + label, + icon, + tone + }: GuidanceProp & { + label: string, + icon: ReactNode, + tone: 'do' | 'dont' + } +): React.JSX.Element => { + return <article className={`guidance-${tone}`}> + {css` + :scope { + min-width: 0; + font: var(${bodyPlus1Font.cssCustomProperty}); + line-height: 21px; + } + :scope > header { + display: flex; + align-items: center; + justify-content: center; + height: 22px; + gap: var(${mediumPadding.cssCustomProperty}); + padding: 0 var(${largePadding.cssCustomProperty}); + border-radius: 4px 4px 0 0; + color: var(${buttonAccentBlockFontColor.cssCustomProperty}); + font: var(${bodyEmphasizedFont.cssCustomProperty}); + text-transform: uppercase; + } + :scope > header > span { + font: inherit; + } + :scope.guidance-do > header { + background: var(${passColor.cssCustomProperty}); + } + :scope.guidance-dont > header { + background: var(${failColor.cssCustomProperty}); + } + :scope > header > *:first-child { + ${iconColor.cssCustomProperty}: var(${buttonAccentBlockFontColor.cssCustomProperty}); + } + :scope .guidance-example { + display: flex; + width: 100%; + min-height: 202px; + flex-direction: column; + align-items: center; + justify-content: center; + margin-top: var(${smallPadding.cssCustomProperty}); + padding: var(${mediumPadding.cssCustomProperty}); + font: var(${bodyPlus1Font.cssCustomProperty}); + line-height: 21px; + } + :scope.guidance-do .guidance-example { + background: color-mix(in srgb, var(${passColor.cssCustomProperty}) 7%, var(${applicationBackgroundColor.cssCustomProperty})); + } + :scope.guidance-dont .guidance-example { + background: color-mix(in srgb, var(${failColor.cssCustomProperty}) 7%, var(${applicationBackgroundColor.cssCustomProperty})); + } + :scope .guidance-example-content { + display: flex; + width: 100%; + min-height: 180px; + align-items: center; + justify-content: center; + padding: calc(var(${standardPadding.cssCustomProperty}) + var(${standardPadding.cssCustomProperty})); + overflow: hidden; + background: var(${applicationBackgroundColor.cssCustomProperty}); + font: var(${bodyPlus1Font.cssCustomProperty}); + line-height: 21px; + } + :scope .guidance-example-content > * { + margin: 0 !important; + } + :scope .guidance-example .story-guidance-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: var(${standardPadding.cssCustomProperty}); + } + :scope .guidance-copy { + padding: var(${standardPadding.cssCustomProperty}) var(${largePadding.cssCustomProperty}) var(${mediumPadding.cssCustomProperty}); + font: var(${bodyPlus1Font.cssCustomProperty}); + line-height: 21px; + } + :scope .guidance-copy > *:first-child { + margin-top: 0; + } + :scope .guidance-copy > *:last-child { + margin-bottom: 0; + } + :scope .guidance-copy > h4 { + margin: 0 0 var(${mediumPadding.cssCustomProperty}); + color: var(${titlePlus1FontColor.cssCustomProperty}); + font: var(${bodyPlus1EmphasizedFont.cssCustomProperty}); + line-height: 21px; + } + :scope .guidance-copy p { + max-width: none; + font: inherit; + line-height: inherit; + letter-spacing: inherit; + } + :scope .guidance-copy ul { + margin: 0; + padding-left: 20px; + } + `} + <header> + {icon} + <span>{label}</span> + </header> + {example ? <div className='guidance-example'><div className='guidance-example-content'>{example}</div></div> : null} + <div className='guidance-copy'> + <h4>{title}</h4> + {children} + </div> + </article>; }; -/** - * Renders a "Dont" section for Storybook documentation. - */ -export const Dont = ({ children }: ChildrenProp): React.JSX.Element => { - return ( - <Container config='48px 1fr'> - <Column> - <NimbleIconExclamationMark style={{ width: '24px', height: '24px' }} severity='error'/> - </Column> - <Column><div>{children}</div></Column> - </Container> - ); +export const Do = ({ children, example, title }: GuidanceProp): React.JSX.Element => { + return <Guidance + label='Do' + example={example} + title={title} + tone='do' + icon={<NimbleIconCheck style={{ width: '16px', height: '14px' }} severity='success'/>} + >{children}</Guidance>; +}; + +export const Dont = ({ children, example, title }: GuidanceProp): React.JSX.Element => { + return <Guidance + label="Don't" + example={example} + title={title} + tone='dont' + icon={<NimbleIconXmark style={{ width: '15px', height: '16px' }} severity='error'/>} + >{children}</Guidance>; }; interface TagProp { diff --git a/packages/storybook/src/vite-env.d.ts b/packages/storybook/src/vite-env.d.ts new file mode 100644 index 0000000000..555a1a22fa --- /dev/null +++ b/packages/storybook/src/vite-env.d.ts @@ -0,0 +1,4 @@ +declare module '*.svg?raw' { + const content: string; + export default content; +} From 014d403aee16dd5681b5f1b01944bed22f3fa524 Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:41:42 -0500 Subject: [PATCH 2/6] argstable font fix --- packages/storybook/src/utilities/story-layout.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/storybook/src/utilities/story-layout.tsx b/packages/storybook/src/utilities/story-layout.tsx index ac9e0e1558..59aaaba786 100644 --- a/packages/storybook/src/utilities/story-layout.tsx +++ b/packages/storybook/src/utilities/story-layout.tsx @@ -282,11 +282,6 @@ export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Ele font-weight: var(${buttonLabelFontWeight.cssCustomProperty}, 600); line-height: 16px; } - :scope p, - :scope li { - font: var(${bodyPlus1Font.cssCustomProperty}); - line-height: 21px; - } :scope a { font: inherit; } From a3851ef0a54bb40e374da6bb66673c68267963ce Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:00:55 -0500 Subject: [PATCH 3/6] add StorySubsection --- .../storybook/src/nimble/button/button.mdx | 79 ++++++++++--------- .../patterns/button/button-styling-docs.mdx | 10 ++- .../storybook/src/nimble/select/select.mdx | 19 +++-- .../storybook/src/utilities/story-layout.tsx | 7 ++ 4 files changed, 71 insertions(+), 44 deletions(-) diff --git a/packages/storybook/src/nimble/button/button.mdx b/packages/storybook/src/nimble/button/button.mdx index 54bbe9ae66..626373df98 100644 --- a/packages/storybook/src/nimble/button/button.mdx +++ b/packages/storybook/src/nimble/button/button.mdx @@ -18,6 +18,7 @@ import { StoryPage, StoryPreview, StorySection, + StorySubsection, Tag } from '../../utilities/story-layout'; @@ -73,18 +74,19 @@ import { </StorySection> <StorySection {...sections.usage}> - <h3 id={sections.whenToUse.id}>{sections.whenToUse.title}</h3> - <div> - <strong>Primary Actions:</strong> Use buttons for primary actions - that users need to take, such as submitting a form, saving changes, - or proceeding to the next step. - </div> + <StorySubsection {...sections.whenToUse}> + <div> + <strong>Primary Actions:</strong> Use buttons for primary actions + that users need to take, such as submitting a form, saving changes, + or proceeding to the next step. + </div> - <div> - <strong>Call to Action (CTA):</strong> Buttons are ideal for CTAs - that encourage users to take a specific action, like "Sign Up," - "Buy Now," or "Learn More." - </div> + <div> + <strong>Call to Action (CTA):</strong> Buttons are ideal for CTAs + that encourage users to take a specific action, like "Sign Up," + "Buy Now," or "Learn More." + </div> + </StorySubsection> </StorySection> <StorySection {...sections.appearanceStyling}> @@ -95,19 +97,21 @@ import { </div> <ButtonStylingDocs components={{ Button: buttonTag }} /> - <h3 id={sections.sizing.id}>{sections.sizing.title}</h3> - <div> - Nimble Buttons are currently always 32px tall. Designs exist for - other sizes; if you need these in an application, please comment on - [Configurable height for nimble controls (#610)](https://github.com/ni/nimble/issues/610). - </div> - <h3 id={sections.spacingWithOtherComponents.id}>{sections.spacingWithOtherComponents.title}</h3> - <div> - Use consistent spacing between elements to keep layouts clean and - organized. Adequate padding around components prevents visual - clutter and helps users focus on key information. Use responsive - spacing when layouts change across screen sizes. - </div> + <StorySubsection {...sections.sizing}> + <div> + Nimble Buttons are currently always 32px tall. Designs exist for + other sizes; if you need these in an application, please comment on + [Configurable height for nimble controls (#610)](https://github.com/ni/nimble/issues/610). + </div> + </StorySubsection> + <StorySubsection {...sections.spacingWithOtherComponents}> + <div> + Use consistent spacing between elements to keep layouts clean and + organized. Adequate padding around components prevents visual + clutter and helps users focus on key information. Use responsive + spacing when layouts change across screen sizes. + </div> + </StorySubsection> </StorySection> <StorySection {...sections.buttonGuidelines}> @@ -162,21 +166,24 @@ import { <StorySection {...sections.accessibility}> <StoryGuidance> - <h3 id={sections.accessibleIconButtons.id}>{sections.accessibleIconButtons.title}</h3> - <ContentHiddenDocs buttonElement="nimble-button" /> - <h3 id={sections.keyboardUsage.id}>{sections.keyboardUsage.title}</h3> - <div> - Buttons must remain keyboard accessible so users can move focus - to them and activate them with standard keyboard controls. - </div> + <StorySubsection {...sections.accessibleIconButtons}> + <ContentHiddenDocs buttonElement="nimble-button" /> + </StorySubsection> + <StorySubsection {...sections.keyboardUsage}> + <div> + Buttons must remain keyboard accessible so users can move focus + to them and activate them with standard keyboard controls. + </div> + </StorySubsection> </StoryGuidance> </StorySection> <StorySection {...sections.alternativesRelated}> - <h3 id={sections.alternativesToConsider.id}>{sections.alternativesToConsider.title}</h3> - <div> - Consider <Tag name={anchorButtonTag} /> if you need a button that - triggers navigation to a URL. - </div> + <StorySubsection {...sections.alternativesToConsider}> + <div> + Consider <Tag name={anchorButtonTag} /> if you need a button that + triggers navigation to a URL. + </div> + </StorySubsection> </StorySection> </StoryPage> diff --git a/packages/storybook/src/nimble/patterns/button/button-styling-docs.mdx b/packages/storybook/src/nimble/patterns/button/button-styling-docs.mdx index 65e86be6fd..3ca7470d3a 100644 --- a/packages/storybook/src/nimble/patterns/button/button-styling-docs.mdx +++ b/packages/storybook/src/nimble/patterns/button/button-styling-docs.mdx @@ -1,8 +1,8 @@ import { NimbleIconKey } from '@ni/nimble-react/icons/key'; -import { StoryShowcase } from '../../../utilities/story-layout'; +import { StoryShowcase, StorySubsection } from '../../../utilities/story-layout'; import { buttonSections } from '../../button/button-sections'; -<h3 id={buttonSections.appearances.id}>{buttonSections.appearances.title}</h3> +<StorySubsection {...buttonSections.appearances}> These are the standard styling options for the button, ordered from least to most visually prominent. Consider each option before employing an appearance @@ -109,7 +109,9 @@ variant. </div> </StoryShowcase> -<h3 id={buttonSections.appearanceVariants.id}>{buttonSections.appearanceVariants.title}</h3> +</StorySubsection> + +<StorySubsection {...buttonSections.appearanceVariants}> Button appearance variants are mainly used when a button needs be distinguished for one of the following reasons: @@ -198,3 +200,5 @@ There are two available values for `appearance-variant`: `primary` and `accent`. To see more examples of appearance variant button hierarchy, see the Figma docs for [Primary and Standard Actions](https://www.figma.com/file/PO9mFOu5BCl8aJvFchEeuN/Nimble_Components?type=design&node-id=1604-74603&mode=design&t=SQ3lyK83VHBUaOkg-0). + +</StorySubsection> diff --git a/packages/storybook/src/nimble/select/select.mdx b/packages/storybook/src/nimble/select/select.mdx index f529f0e641..dd998e8ca9 100644 --- a/packages/storybook/src/nimble/select/select.mdx +++ b/packages/storybook/src/nimble/select/select.mdx @@ -18,6 +18,7 @@ import { StoryPage, StoryPreview, StorySection, + StorySubsection, Tag } from '../../utilities/story-layout'; @@ -63,12 +64,13 @@ in addition to selecting from the list. </StoryPreview> <StoryApi> -<h3 id={sections.api.id}>{sections.api.title}</h3> +<StorySubsection {...sections.api}> <Controls of={selectStories.select} /> <ComponentApisLink /> +</StorySubsection> -<h3 id={sections.listOption.id}>{sections.listOption.title}</h3> +<StorySubsection {...sections.listOption}> <Description of={listOptionStories} /> <Controls of={listOptionStories.listOption} /> @@ -83,11 +85,15 @@ on the Select control. Without this, the Select may display unexpected behavior (like reverting to the placeholder value when updating the list of filtered options). -<h3 id={sections.listOptionGroup.id}>{sections.listOptionGroup.title}</h3> +</StorySubsection> + +<StorySubsection {...sections.listOptionGroup}> <Description of={listOptionGroupStories} /> <Controls of={listOptionGroupStories.listOptionGroup} /> +</StorySubsection> + </StoryApi> </StorySection> @@ -103,7 +109,7 @@ the control is wide enough so labels do not wrap. <StorySection {...sections.examples}> -<h3 id={sections.placeholder.id}>{sections.placeholder.title}</h3> +<StorySubsection {...sections.placeholder}> To display placeholder text within the select you must provide an option that has the `disabled`, `selected` and `hidden` attributes set. This option will not @@ -124,8 +130,9 @@ in the list. See [#2783](https://github.com/ni/nimble/issues/2783) for more deta <StoryPreview> <Canvas of={selectStories.placeholder} /> </StoryPreview> +</StorySubsection> -<h3 id={sections.dynamicOptions.id}>{sections.dynamicOptions.title}</h3> +<StorySubsection {...sections.dynamicOptions}> There are scenarios that require not all possible options to be available initially, such as when the set of all options is too large to be loaded at @@ -167,6 +174,8 @@ group should be displayed, and all options within the group should be displayed as well. If the group label does not match the filter text, then options within the group should only be displayed if they match the filter text. +</StorySubsection> + </StorySection> </StoryPage> diff --git a/packages/storybook/src/utilities/story-layout.tsx b/packages/storybook/src/utilities/story-layout.tsx index 59aaaba786..b9fe405034 100644 --- a/packages/storybook/src/utilities/story-layout.tsx +++ b/packages/storybook/src/utilities/story-layout.tsx @@ -311,6 +311,13 @@ export const StorySection = ({ id, title, children }: StorySectionProps): React. </section>; }; +export const StorySubsection = ({ id, title, children }: StorySectionProps): React.JSX.Element => { + return <section> + <h3 id={id}>{title}</h3> + {children} + </section>; +}; + export const StoryPreview = ({ children }: ChildrenProp): React.JSX.Element => { return <div> {css` From 70bb0e4c96fc6a54534401592b0f7739b93464ab Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Wed, 26 Aug 2026 23:15:49 -0500 Subject: [PATCH 4/6] Refine Storybook component documentation layout Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/nimble/button/button-anatomy.tsx | 77 +++++++++++++++---- .../src/nimble/button/button-sections.ts | 2 +- .../storybook/src/nimble/button/button.mdx | 2 +- .../storybook/src/utilities/story-layout.tsx | 56 +++++++++++--- 4 files changed, 111 insertions(+), 26 deletions(-) diff --git a/packages/storybook/src/nimble/button/button-anatomy.tsx b/packages/storybook/src/nimble/button/button-anatomy.tsx index 0147dc1cc1..e25fbaa8d0 100644 --- a/packages/storybook/src/nimble/button/button-anatomy.tsx +++ b/packages/storybook/src/nimble/button/button-anatomy.tsx @@ -2,19 +2,58 @@ import React from 'react'; import { applicationBackgroundColor, bodyFontColor, - buttonAccentBlockFontColor, - tagFillColor + tagFillColor, + cardBorderColor, + dividerWidth, + standardPadding } from '@ni/nimble-components/dist/esm/theme-provider/design-tokens'; import anatomySvg from './images/button-anatomy.svg?raw'; -const anatomySvgStyle = { - display: 'block', - maxWidth: '100%', - paddingBottom: '16px', +const anatomySvgContent = anatomySvg.slice( + anatomySvg.indexOf('>') + 1, + anatomySvg.lastIndexOf('</svg>') +); + +const anatomyStyle = { + display: 'flex', + flexDirection: 'column', + gap: `var(${standardPadding.cssCustomProperty})`, + paddingBottom: `var(${standardPadding.cssCustomProperty})`, + alignItems: 'stretch', + width: '100%', '--button-anatomy-background-color': `var(${applicationBackgroundColor.cssCustomProperty})`, '--button-anatomy-body-color': `var(${bodyFontColor.cssCustomProperty})`, '--button-anatomy-tag-color': `var(${tagFillColor.cssCustomProperty})`, - '--button-anatomy-accent-color': `var(${buttonAccentBlockFontColor.cssCustomProperty})` + '--button-anatomy-accent-color': `var(${bodyFontColor.cssCustomProperty})` +} as React.CSSProperties; + +const controlWindowStyle = { + position: 'relative', + display: 'block', + width: '100%', + height: '180px', + border: `var(${dividerWidth.cssCustomProperty}) solid var(${cardBorderColor.cssCustomProperty})`, + borderRadius: '4px', + overflow: 'hidden', + background: 'var(--button-anatomy-background-color)' +} as React.CSSProperties; + +const controlStyle = { + position: 'absolute', + top: '70px', + left: 'calc(50% - 96px)', + display: 'block', + width: '151px', + height: '85px', + fill: 'none' +} as React.CSSProperties; + +const legendStyle = { + display: 'block', + width: '91px', + height: '65px', + marginLeft: `var(${standardPadding.cssCustomProperty})`, + fill: 'none' } as React.CSSProperties; interface ButtonAnatomyProps { @@ -22,10 +61,22 @@ interface ButtonAnatomyProps { } export const ButtonAnatomy = ({ alt }: ButtonAnatomyProps): React.JSX.Element => { - return <div - aria-label={alt} - role='img' - style={anatomySvgStyle} - dangerouslySetInnerHTML={{ __html: anatomySvg }} - />; + return <div aria-label={alt} role='img' style={anatomyStyle}> + <div style={controlWindowStyle}> + <svg + aria-hidden='true' + viewBox='248 120 151 85' + xmlns='http://www.w3.org/2000/svg' + style={controlStyle} + dangerouslySetInnerHTML={{ __html: anatomySvgContent }} + /> + </div> + <svg + aria-hidden='true' + viewBox='54 260 91 65' + xmlns='http://www.w3.org/2000/svg' + style={legendStyle} + dangerouslySetInnerHTML={{ __html: anatomySvgContent }} + /> + </div>; }; diff --git a/packages/storybook/src/nimble/button/button-sections.ts b/packages/storybook/src/nimble/button/button-sections.ts index 7bc9075bfd..0f030949cf 100644 --- a/packages/storybook/src/nimble/button/button-sections.ts +++ b/packages/storybook/src/nimble/button/button-sections.ts @@ -1,6 +1,6 @@ export const buttonSections = { overview: { id: 'overview', title: 'Overview', level: 2 }, - buttonAnatomy: { id: 'button-anatomy', title: 'Button Anatomy', level: 2 }, + buttonAnatomy: { id: 'button-anatomy', title: 'Button Anatomy', level: 3 }, implementationApi: { id: 'implementation-api', title: 'Implementation & API', level: 2 }, usage: { id: 'usage', title: 'Usage', level: 2 }, whenToUse: { id: 'when-to-use', title: 'When to Use', level: 3 }, diff --git a/packages/storybook/src/nimble/button/button.mdx b/packages/storybook/src/nimble/button/button.mdx index 626373df98..e9287642e7 100644 --- a/packages/storybook/src/nimble/button/button.mdx +++ b/packages/storybook/src/nimble/button/button.mdx @@ -58,7 +58,7 @@ import { </StorySection> <StorySection {...sections.buttonAnatomy}> - <StoryAnatomy alt="Button anatomy showing the Container, Icon, and Label parts of a button."> + <StoryAnatomy alt="Button anatomy showing the Container, Icon, and Label parts of a button." bordered={false}> <ButtonAnatomy alt="Button anatomy showing the Container, Icon, and Label parts of a button." /> </StoryAnatomy> </StorySection> diff --git a/packages/storybook/src/utilities/story-layout.tsx b/packages/storybook/src/utilities/story-layout.tsx index b9fe405034..67e4cee478 100644 --- a/packages/storybook/src/utilities/story-layout.tsx +++ b/packages/storybook/src/utilities/story-layout.tsx @@ -38,6 +38,8 @@ import { headlinePlus1FontColor, iconColor, } from '@ni/nimble-components/dist/esm/theme-provider/design-tokens'; +import { tokenNames } from '@ni/nimble-components/dist/esm/theme-provider/design-token-names'; +import { tokenValues } from '@ni/nimble-components/dist/esm/theme-provider/design-token-values'; export const css = (strings: TemplateStringsArray, ...values: unknown[]): React.JSX.Element => <style> {`@scope {${String.raw({ raw: strings }, ...values)}}`} @@ -53,9 +55,24 @@ const getPreferredTheme = (): 'dark' | 'light' => ( export const theme = getPreferredTheme(); +interface StoryTokenStyles extends React.CSSProperties { + [name: `--ni-nimble-${string}`]: string; +} + +const storyTokenStyles = (Object.keys(tokenValues) as (keyof typeof tokenValues)[]).reduce<StoryTokenStyles>( + (styles, tokenName) => { + const cssTokenName = tokenNames[tokenName]; + const value = tokenValues[tokenName]; + styles[`--ni-nimble-${cssTokenName}`] = typeof value === 'string' ? value : value[theme]; + return styles; + }, + {} +); + interface StorySectionProps extends ChildrenProp { id: string; title: string; + level?: 2 | 3; } export interface StoryTocItem { @@ -126,7 +143,8 @@ export const StoryTableOfContents = ({ items }: { items: readonly StoryTocItem[] } :scope h2 { margin: 0 0 var(${standardPadding.cssCustomProperty}); - padding-inline-start: var(${mediumPadding.cssCustomProperty}); + padding: 0; + border: 0; color: var(${bodyDisabledFontColor.cssCustomProperty}); font: var(${buttonLabelFont.cssCustomProperty}); text-transform: uppercase; @@ -193,7 +211,7 @@ export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Ele }, []); return <NimbleThemeProvider ref={themeProviderRef} theme={theme}> - <div className='story-page'> + <div className='story-page' style={storyTokenStyles}> {css` :scope { position: relative; @@ -205,7 +223,7 @@ export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Ele :scope > .story-page-toc { position: absolute; inset-block: 0; - inset-inline-start: calc(100% + var(${standardPadding.cssCustomProperty})); + inset-inline-start: calc(100% + var(${largePadding.cssCustomProperty}) + var(${standardPadding.cssCustomProperty})); width: max-content; } :scope > .story-page-content { @@ -247,7 +265,7 @@ export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Ele letter-spacing: -1px; } :scope h2 { - margin: calc(var(${largePadding.cssCustomProperty}) + var(${standardPadding.cssCustomProperty})) 0 var(${largePadding.cssCustomProperty}); + margin: 36px 0 var(${largePadding.cssCustomProperty}); scroll-margin-block-start: var(${largePadding.cssCustomProperty}); padding-bottom: calc(var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})); border-bottom: var(${dividerWidth.cssCustomProperty}) solid var(${dividerBackgroundColor.cssCustomProperty}); @@ -257,6 +275,9 @@ export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Ele font-weight: var(${bodyPlus1FontWeight.cssCustomProperty}); line-height: 34px; } + :scope h2:first-of-type { + margin-top: 36px; + } :scope h3 { margin: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})) 0 var(${standardPadding.cssCustomProperty}); scroll-margin-block-start: var(${largePadding.cssCustomProperty}); @@ -267,6 +288,9 @@ export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Ele line-height: 26px; letter-spacing: -1px; } + :scope h3:first-of-type { + margin-top: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})); + } :scope h4 { margin: var(${largePadding.cssCustomProperty}) 0 calc(var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})); color: var(${bodyEmphasizedFontColor.cssCustomProperty}); @@ -296,6 +320,7 @@ export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Ele } :scope code { font: var(${bodyEmphasizedFont.cssCustomProperty}); + padding-top: 2px; } `} {children} @@ -304,9 +329,10 @@ export const StoryPage = ({ children, toc = [] }: StoryPageProps): React.JSX.Ele </NimbleThemeProvider>; }; -export const StorySection = ({ id, title, children }: StorySectionProps): React.JSX.Element => { +export const StorySection = ({ id, title, level = 2, children }: StorySectionProps): React.JSX.Element => { + const Heading = level === 3 ? 'h3' : 'h2'; return <section> - <h2 id={id}>{title}</h2> + <Heading id={id}>{title}</Heading> {children} </section>; }; @@ -343,6 +369,9 @@ export const StoryApi = ({ children }: ChildrenProp): React.JSX.Element => { margin: var(${largePadding.cssCustomProperty}) 0 calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty})); overflow-x: auto; } + :scope .docblock-argstable { + margin-inline: 0; + } :scope > * { margin-top: var(${standardPadding.cssCustomProperty}); } @@ -357,23 +386,27 @@ export const StoryApi = ({ children }: ChildrenProp): React.JSX.Element => { interface StoryAnatomyProps extends ChildrenProp { src?: string; alt: string; + bordered?: boolean; } -export const StoryAnatomy = ({ children, src, alt }: StoryAnatomyProps): React.JSX.Element => { +export const StoryAnatomy = ({ children, src, alt, bordered = true }: StoryAnatomyProps): React.JSX.Element => { return <figure> {css` :scope { margin: calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty}) + var(${smallPadding.cssCustomProperty})) 0 calc(var(${largePadding.cssCustomProperty}) + var(${mediumPadding.cssCustomProperty})); - border: var(${dividerWidth.cssCustomProperty}) solid var(${cardBorderColor.cssCustomProperty}); - border-radius: 4px; - overflow: hidden; - background: var(${applicationBackgroundColor.cssCustomProperty}); + border: ${bordered ? `var(${dividerWidth.cssCustomProperty}) solid var(${cardBorderColor.cssCustomProperty})` : '0'}; + border-radius: ${bordered ? '4px' : '0'}; + overflow: ${bordered ? 'hidden' : 'visible'}; + background: ${bordered ? `var(${applicationBackgroundColor.cssCustomProperty})` : 'transparent'}; } :scope img { display: block; width: 100%; height: auto; } + :scope > :not(style) { + margin-inline: auto; + } `} {children ?? (src ? <img src={src} alt={alt}/> : null)} </figure>; @@ -618,6 +651,7 @@ const Guidance = ( border-radius: 4px 4px 0 0; color: var(${buttonAccentBlockFontColor.cssCustomProperty}); font: var(${bodyEmphasizedFont.cssCustomProperty}); + font-weight: 700; text-transform: uppercase; } :scope > header > span { From 97247b86c05da4dc0c6784fac751300296fe7705 Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Wed, 26 Aug 2026 23:18:23 -0500 Subject: [PATCH 5/6] Scope custom Storybook TOC configuration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/storybook/.storybook/preview.js | 2 +- packages/storybook/src/nimble/button/button.mdx | 2 +- packages/storybook/src/nimble/select/select.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/storybook/.storybook/preview.js b/packages/storybook/.storybook/preview.js index 72b76015a6..e21ba76af7 100644 --- a/packages/storybook/.storybook/preview.js +++ b/packages/storybook/.storybook/preview.js @@ -53,7 +53,7 @@ const parameters = { }, docs: { controls: { sort: 'alpha' }, - toc: { disable: true }, + toc: { headingSelector: 'h1, h2, h3' }, theme } }; diff --git a/packages/storybook/src/nimble/button/button.mdx b/packages/storybook/src/nimble/button/button.mdx index e9287642e7..cbe4381acb 100644 --- a/packages/storybook/src/nimble/button/button.mdx +++ b/packages/storybook/src/nimble/button/button.mdx @@ -22,7 +22,7 @@ import { Tag } from '../../utilities/story-layout'; -<Meta of={buttonStories} /> +<Meta of={buttonStories} parameters={{ docs: { toc: { disable: true } } }} /> <StoryPage toc={[ sections.overview, sections.buttonAnatomy, diff --git a/packages/storybook/src/nimble/select/select.mdx b/packages/storybook/src/nimble/select/select.mdx index dd998e8ca9..dc7af3aad4 100644 --- a/packages/storybook/src/nimble/select/select.mdx +++ b/packages/storybook/src/nimble/select/select.mdx @@ -34,7 +34,7 @@ export const sections = { dynamicOptions: { id: 'dynamic-options', title: 'Dynamic options', level: 3 } }; -<Meta of={selectStories} /> +<Meta of={selectStories} parameters={{ docs: { toc: { disable: true } } }} /> <StoryPage toc={[ sections.overview, sections.implementationApi, From 5d1c21b01e92563819b772c732bb8ef2617eae67 Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Fri, 28 Aug 2026 08:24:48 -0500 Subject: [PATCH 6/6] Fix duplicate Storybook TOCs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/storybook/src/nimble/button/button.mdx | 2 +- packages/storybook/src/nimble/button/button.stories.ts | 3 +++ packages/storybook/src/nimble/select/select.mdx | 2 +- packages/storybook/src/nimble/select/select.stories.ts | 5 +++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/storybook/src/nimble/button/button.mdx b/packages/storybook/src/nimble/button/button.mdx index cbe4381acb..e9287642e7 100644 --- a/packages/storybook/src/nimble/button/button.mdx +++ b/packages/storybook/src/nimble/button/button.mdx @@ -22,7 +22,7 @@ import { Tag } from '../../utilities/story-layout'; -<Meta of={buttonStories} parameters={{ docs: { toc: { disable: true } } }} /> +<Meta of={buttonStories} /> <StoryPage toc={[ sections.overview, sections.buttonAnatomy, diff --git a/packages/storybook/src/nimble/button/button.stories.ts b/packages/storybook/src/nimble/button/button.stories.ts index b3205047af..5b44b051de 100644 --- a/packages/storybook/src/nimble/button/button.stories.ts +++ b/packages/storybook/src/nimble/button/button.stories.ts @@ -39,6 +39,9 @@ const metadata: Meta<ButtonArgs> = { parameters: { actions: { handles: ['click'] + }, + docs: { + toc: { disable: true } } }, argTypes: { diff --git a/packages/storybook/src/nimble/select/select.mdx b/packages/storybook/src/nimble/select/select.mdx index dc7af3aad4..dd998e8ca9 100644 --- a/packages/storybook/src/nimble/select/select.mdx +++ b/packages/storybook/src/nimble/select/select.mdx @@ -34,7 +34,7 @@ export const sections = { dynamicOptions: { id: 'dynamic-options', title: 'Dynamic options', level: 3 } }; -<Meta of={selectStories} parameters={{ docs: { toc: { disable: true } } }} /> +<Meta of={selectStories} /> <StoryPage toc={[ sections.overview, sections.implementationApi, diff --git a/packages/storybook/src/nimble/select/select.stories.ts b/packages/storybook/src/nimble/select/select.stories.ts index 4bbcfcd39f..9f6d92b124 100644 --- a/packages/storybook/src/nimble/select/select.stories.ts +++ b/packages/storybook/src/nimble/select/select.stories.ts @@ -138,6 +138,11 @@ When the \`loading-visible\` attribute is set, a loading spinner will be display const metadata: Meta<SelectArgs> = { title: 'Components/Select', + parameters: { + docs: { + toc: { disable: true } + } + }, render: createUserSelectedThemeStory(html` ${disableStorybookZoomTransform} <${selectTag}