├── ui
├── .nvmrc
├── .eslintignore
├── src
│ ├── css
│ │ ├── _THEMES
│ │ │ ├── theme1
│ │ │ │ ├── overrides
│ │ │ │ │ ├── base
│ │ │ │ │ │ ├── index.css
│ │ │ │ │ │ └── type.css
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── index.css
│ │ │ │ │ │ └── skin.css
│ │ │ │ │ └── index.css
│ │ │ │ ├── fonts
│ │ │ │ │ ├── roboto-medium
│ │ │ │ │ │ └── roboto-medium.woff
│ │ │ │ │ └── roboto-regular
│ │ │ │ │ │ └── roboto-regular.woff
│ │ │ │ ├── tokens.css
│ │ │ │ └── index.css
│ │ │ └── default
│ │ │ │ ├── index.css
│ │ │ │ └── overrides
│ │ │ │ └── index.css
│ │ ├── components
│ │ │ ├── image.css
│ │ │ ├── button-group.css
│ │ │ ├── ajax.css
│ │ │ ├── responsive-media.css
│ │ │ ├── skiplinks.css
│ │ │ ├── search.css
│ │ │ ├── index.css
│ │ │ ├── tables.css
│ │ │ ├── pagination.css
│ │ │ ├── breadcrumb.css
│ │ │ ├── image-gallery.css
│ │ │ ├── carousel.css
│ │ │ ├── accordion.css
│ │ │ ├── skin.css
│ │ │ ├── modal.css
│ │ │ ├── card.css
│ │ │ └── banner.css
│ │ ├── utilities
│ │ │ ├── index.css
│ │ │ ├── layout.css
│ │ │ └── display.css
│ │ ├── base
│ │ │ ├── index.css
│ │ │ ├── colors.css
│ │ │ ├── config.css
│ │ │ └── type.css
│ │ ├── web-components
│ │ │ ├── webui-disclosure.css
│ │ │ ├── webui-predictive-search.css
│ │ │ ├── webui-fetch-html.css
│ │ │ ├── webui-share.css
│ │ │ ├── index.css
│ │ │ ├── webui-video-player.css
│ │ │ ├── webui-prose.css
│ │ │ ├── webui-make-clickable.css
│ │ │ ├── webui-notify.css
│ │ │ ├── webui-ajax-loader.css
│ │ │ ├── webui-range-input.css
│ │ │ ├── webui-toggle.css
│ │ │ └── webui-carousel.css
│ │ ├── form
│ │ │ ├── input.css
│ │ │ ├── select.css
│ │ │ ├── textarea.css
│ │ │ ├── fieldset.css
│ │ │ ├── label.css
│ │ │ ├── index.css
│ │ │ ├── radio.css
│ │ │ └── checkbox.css
│ │ ├── global
│ │ │ ├── index.css
│ │ │ └── icon.css
│ │ ├── layout
│ │ │ ├── index.css
│ │ │ ├── wrapper.css
│ │ │ ├── page.css
│ │ │ ├── sidebar.css
│ │ │ ├── stack.css
│ │ │ └── flex.css
│ │ └── tokens.css
│ ├── images
│ │ └── interface
│ │ │ └── favicon.png
│ ├── fonts
│ │ └── roboto-light
│ │ │ └── roboto-light.woff
│ └── javascript
│ │ ├── config
│ │ ├── main.ts
│ │ └── browser-supports-features.ts
│ │ ├── utils
│ │ └── polyfills.ts
│ │ ├── templates
│ │ └── search-results.ts
│ │ ├── web-components
│ │ ├── webui-notify.ts
│ │ ├── webui-ajax-loader.ts
│ │ ├── webui-toggle.ts
│ │ ├── webui-prose.ts
│ │ ├── webui-make-clickable.ts
│ │ ├── webui-range-input.ts
│ │ └── webui-fetch-html.ts
│ │ └── index.ts
├── .env.development
├── .husky
│ ├── pre-commit
│ └── _
│ │ └── husky.sh
├── .prettierrc
├── .npmrc
├── .storybook
│ ├── theme
│ │ ├── logo.png
│ │ └── index.js
│ ├── manager.js
│ ├── preview-head.html
│ ├── main.js
│ └── preview.js
├── public
│ ├── theme-switcher.png
│ ├── .gitignore
│ ├── ajax
│ │ └── ajax.html
│ └── storybook-overrides.css
├── .env.production
├── stories
│ ├── 6. Web Components Or Custom Elements
│ │ ├── WebUI Ajax Loader
│ │ │ ├── WebUIAjaxLoader.js
│ │ │ ├── WebUIAjaxLoader.stories.js
│ │ │ └── WebUIAjaxLoader.mdx
│ │ ├── WebUI Prose
│ │ │ ├── WebUIProse.stories.js
│ │ │ └── WebUIProse.mdx
│ │ ├── WebUI Modal
│ │ │ ├── WebUIModal.stories.js
│ │ │ ├── WebUIModal.mdx
│ │ │ └── WebUIModal.js
│ │ ├── WebUI Fetch Html
│ │ │ ├── WebUIFetchHtml.stories.js
│ │ │ ├── WebUIFetchHtml.js
│ │ │ └── WebUIFetchHtml.mdx
│ │ ├── WebUI Video Player
│ │ │ ├── WebUIVideoPlayer.stories.js
│ │ │ └── WebUIVideoPlayer.mdx
│ │ ├── WebUI Range Input
│ │ │ ├── WebUIRangeInput.stories.js
│ │ │ ├── WebUIRangeInput.js
│ │ │ └── WebUIRangeInput.mdx
│ │ ├── WebUI Form Validate
│ │ │ ├── WebUIFormValidate.stories.js
│ │ │ └── WebUIFormValidate.mdx
│ │ ├── WebUI Make Clickable
│ │ │ ├── WebUIMakeClickable.stories.js
│ │ │ └── WebUIMakeClickable.mdx
│ │ ├── WebUI Predictive Search
│ │ │ ├── WebUIPredictiveSearch.stories.js
│ │ │ ├── WebUIPredictiveSearch.js
│ │ │ └── WebUIPredictiveSearch.mdx
│ │ ├── WebUI Tabs
│ │ │ ├── WebUITabs.stories.js
│ │ │ └── WebUITabs.mdx
│ │ ├── WebUI Toggle
│ │ │ ├── WebUIToggle.mdx
│ │ │ ├── WebUIToggle.stories.js
│ │ │ └── WebUIToggle.js
│ │ ├── WebUI Share
│ │ │ ├── WebUIShare.stories.js
│ │ │ └── WebUIShare.mdx
│ │ ├── WebUI Disclosure
│ │ │ ├── WebUIDisclosure.js
│ │ │ ├── WebUIDisclosure.stories.js
│ │ │ └── WebUIDisclosure.mdx
│ │ ├── WebUI Notify
│ │ │ ├── WebUINotify.stories.js
│ │ │ ├── WebUINotify.mdx
│ │ │ └── WebUINotify.js
│ │ ├── WebUI Carousel
│ │ │ ├── WebUICarousel.stories.js
│ │ │ └── WebUICarousel.mdx
│ │ └── WebComponents.mdx
│ ├── 4. Forms
│ │ ├── Radio
│ │ │ ├── Radio.mdx
│ │ │ ├── Radio.stories.js
│ │ │ └── Radio.js
│ │ ├── Textarea
│ │ │ ├── Textarea.js
│ │ │ ├── Textarea.mdx
│ │ │ └── Textarea.stories.js
│ │ ├── Select
│ │ │ ├── Select.mdx
│ │ │ ├── Select.stories.js
│ │ │ └── Select.js
│ │ ├── Checkbox
│ │ │ ├── Checkbox.mdx
│ │ │ ├── Checkbox.js
│ │ │ └── Checkbox.stories.js
│ │ ├── DateOfBirth
│ │ │ ├── DateOfBirth.stories.js
│ │ │ └── DateOfBirth.mdx
│ │ ├── Input
│ │ │ ├── Input.mdx
│ │ │ ├── Input.stories.js
│ │ │ └── Input.js
│ │ └── Form
│ │ │ ├── FormNoArgs.stories.js
│ │ │ ├── Form.stories.js
│ │ │ └── Form.mdx
│ ├── 5. Components
│ │ ├── Pagination
│ │ │ ├── Pagination.mdx
│ │ │ ├── Pagination.stories.js
│ │ │ └── Pagination.js
│ │ ├── Skiplinks
│ │ │ ├── Skiplinks.stories.js
│ │ │ ├── Skiplinks.js
│ │ │ └── Skiplinks.mdx
│ │ ├── Breadcrumb
│ │ │ ├── Breadcrumb.stories.js
│ │ │ ├── Breadcrumb.mdx
│ │ │ └── Breadcrumb.js
│ │ ├── Image Gallery
│ │ │ ├── ImageGallery.stories.js
│ │ │ └── ImageGallery.mdx
│ │ ├── Buttons & Links
│ │ │ ├── Button Group
│ │ │ │ ├── ButtonGroup.mdx
│ │ │ │ ├── ButtonGroup.stories.js
│ │ │ │ └── ButtonGroup.js
│ │ │ ├── Links
│ │ │ │ ├── Link.mdx
│ │ │ │ ├── IconLink.stories.js
│ │ │ │ ├── Link.js
│ │ │ │ ├── Link.stories.js
│ │ │ │ └── TextIconLink.stories.js
│ │ │ └── Buttons
│ │ │ │ ├── Button.mdx
│ │ │ │ ├── IconButton.stories.js
│ │ │ │ ├── Button.js
│ │ │ │ └── Button.stories.js
│ │ ├── Tables
│ │ │ ├── Table.stories.js
│ │ │ └── Table.mdx
│ │ ├── Responsive Media
│ │ │ ├── ResponsiveMedia.stories.js
│ │ │ ├── ResponsiveMedia.js
│ │ │ └── ResponsiveMedia.mdx
│ │ ├── Cards
│ │ │ ├── CardsNoArgs.stories.js
│ │ │ └── Cards.stories.js
│ │ ├── Skin
│ │ │ ├── Skin.mdx
│ │ │ ├── Skin.js
│ │ │ └── Skin.stories.js
│ │ ├── Images
│ │ │ ├── Images.stories.js
│ │ │ ├── Images.mdx
│ │ │ └── Images.js
│ │ ├── Banners
│ │ │ ├── Banners.stories.js
│ │ │ └── Banners.mdx
│ │ ├── Accordion
│ │ │ ├── Accordion.stories.js
│ │ │ └── Accordion.mdx
│ │ ├── Modal
│ │ │ └── Modal.stories.js
│ │ ├── Search
│ │ │ ├── Search.mdx
│ │ │ ├── Search.stories.js
│ │ │ └── Search.js
│ │ ├── Navigation
│ │ │ └── Navigation.stories.js
│ │ └── Carousel
│ │ │ ├── Carousel.stories.js
│ │ │ └── Carousel.mdx
│ ├── 8. Pages
│ │ └── Homepage.stories.js
│ ├── 2. Foundations
│ │ ├── Colours
│ │ │ ├── Colours.stories.js
│ │ │ └── Colours.mdx
│ │ ├── Typography
│ │ │ ├── Typography.stories.js
│ │ │ └── Typography.mdx
│ │ └── Icons
│ │ │ ├── Icons.stories.js
│ │ │ └── Icons.mdx
│ ├── 3. Layout
│ │ ├── Sidebar
│ │ │ ├── Sidebar.stories.js
│ │ │ ├── Sidebar.mdx
│ │ │ └── Sidebar.js
│ │ ├── MarginPadding
│ │ │ ├── MarginPadding.mdx
│ │ │ ├── MarginPadding.js
│ │ │ └── MarginPadding.stories.js
│ │ ├── Stack
│ │ │ ├── Stack.js
│ │ │ ├── Stack.stories.js
│ │ │ └── Stack.mdx
│ │ ├── Page
│ │ │ ├── Page.stories.js
│ │ │ └── Page.mdx
│ │ ├── Flex
│ │ │ ├── Flex.mdx
│ │ │ ├── Flex.js
│ │ │ └── Flex.stories.js
│ │ ├── Wrapper
│ │ │ ├── Wrapper.mdx
│ │ │ ├── Wrapper.js
│ │ │ └── Wrapper.stories.js
│ │ └── Grid
│ │ │ └── Grid.stories.js
│ └── 7. Utilities
│ │ ├── Utilities.stories.js
│ │ ├── Utilities.mdx
│ │ └── Utilities.js
├── scripts
│ ├── theme-config.js
│ ├── static-assets-config.js
│ └── bundle-all-themes.js
├── types.d.ts
├── tsconfig.json
├── index.html
├── .eslintrc.js
└── .stylelintrc
├── .editorconfig
├── .gitignore
├── .vscode
└── settings.json
└── .github
└── workflows
└── static.yml
/ui/.nvmrc:
--------------------------------------------------------------------------------
1 | 20.11.0
2 |
--------------------------------------------------------------------------------
/ui/.eslintignore:
--------------------------------------------------------------------------------
1 | # Ignore files/folders
2 | .eslintrc.js
3 | stories
4 |
--------------------------------------------------------------------------------
/ui/src/css/_THEMES/theme1/overrides/base/index.css:
--------------------------------------------------------------------------------
1 | @import 'type';
2 |
--------------------------------------------------------------------------------
/ui/src/css/_THEMES/theme1/overrides/components/index.css:
--------------------------------------------------------------------------------
1 | @import 'skin';
2 |
--------------------------------------------------------------------------------
/ui/src/css/components/image.css:
--------------------------------------------------------------------------------
1 | .image {
2 | object-fit: cover;
3 | }
4 |
--------------------------------------------------------------------------------
/ui/src/css/utilities/index.css:
--------------------------------------------------------------------------------
1 | @import 'display';
2 | @import 'layout';
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 | [*]
3 | indent_style = space
4 | indent_size = 4
5 |
--------------------------------------------------------------------------------
/ui/.env.development:
--------------------------------------------------------------------------------
1 | STORYBOOK_CSS_PATH=index.css
2 | STORYBOOK_JS_PATH=index.js
3 |
--------------------------------------------------------------------------------
/ui/src/css/base/index.css:
--------------------------------------------------------------------------------
1 | @import 'colors';
2 | @import 'config';
3 | @import 'type';
4 |
--------------------------------------------------------------------------------
/ui/src/css/components/button-group.css:
--------------------------------------------------------------------------------
1 | .button-group {
2 | align-items: center;
3 | }
4 |
--------------------------------------------------------------------------------
/ui/src/css/web-components/webui-disclosure.css:
--------------------------------------------------------------------------------
1 | webui-disclosure {
2 | /* n/a */
3 | }
4 |
--------------------------------------------------------------------------------
/ui/src/css/components/ajax.css:
--------------------------------------------------------------------------------
1 | .ajax__error {
2 | color: var(--webui-color-error-500);
3 | }
4 |
--------------------------------------------------------------------------------
/ui/src/css/form/input.css:
--------------------------------------------------------------------------------
1 | .input {
2 | /* Basic component styles are in "form.css". */
3 | }
4 |
--------------------------------------------------------------------------------
/ui/src/css/form/select.css:
--------------------------------------------------------------------------------
1 | .select {
2 | /* Basic component styles are in "form.css". */
3 | }
4 |
--------------------------------------------------------------------------------
/ui/src/css/form/textarea.css:
--------------------------------------------------------------------------------
1 | .textarea {
2 | /* Basic component styles are in "form.css". */
3 | }
4 |
--------------------------------------------------------------------------------
/ui/src/css/web-components/webui-predictive-search.css:
--------------------------------------------------------------------------------
1 | webui-predictive-search {
2 | /* N/A */
3 | }
4 |
--------------------------------------------------------------------------------
/ui/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | cd ./ui && npx lint-staged
5 |
--------------------------------------------------------------------------------
/ui/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "tabWidth": 4,
4 | "trailingComma": "all"
5 | }
6 |
--------------------------------------------------------------------------------
/ui/.npmrc:
--------------------------------------------------------------------------------
1 | # Config variable. Allows theme to be passed as NPM script param.
2 | web-ui-boilerplate:theme=default
3 |
--------------------------------------------------------------------------------
/ui/.storybook/theme/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/basher/Web-UI-Boilerplate/HEAD/ui/.storybook/theme/logo.png
--------------------------------------------------------------------------------
/ui/public/theme-switcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/basher/Web-UI-Boilerplate/HEAD/ui/public/theme-switcher.png
--------------------------------------------------------------------------------
/ui/src/css/global/index.css:
--------------------------------------------------------------------------------
1 | /* Import 'reset' first. */
2 | @import 'reset';
3 | @import 'icon';
4 | @import 'typography';
5 |
--------------------------------------------------------------------------------
/ui/.env.production:
--------------------------------------------------------------------------------
1 | STORYBOOK_CSS_PATH=build/ui/default/css/index.css
2 | STORYBOOK_JS_PATH=build/ui/default/javascript/index.js
3 |
--------------------------------------------------------------------------------
/ui/src/css/form/fieldset.css:
--------------------------------------------------------------------------------
1 | .fieldset {
2 | --flex-gap: var(--webui-gutter-s);
3 |
4 | flex-direction: column;
5 | }
6 |
--------------------------------------------------------------------------------
/ui/src/images/interface/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/basher/Web-UI-Boilerplate/HEAD/ui/src/images/interface/favicon.png
--------------------------------------------------------------------------------
/ui/src/fonts/roboto-light/roboto-light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/basher/Web-UI-Boilerplate/HEAD/ui/src/fonts/roboto-light/roboto-light.woff
--------------------------------------------------------------------------------
/ui/src/javascript/config/main.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | message: {
3 | error: 'Error...',
4 | success: 'Success...',
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/ui/src/css/form/label.css:
--------------------------------------------------------------------------------
1 | .label {
2 | display: block;
3 | }
4 |
5 | .label__hint {
6 | display: block;
7 | font-size: var(--webui-font-size-s);
8 | }
9 |
--------------------------------------------------------------------------------
/ui/src/css/components/responsive-media.css:
--------------------------------------------------------------------------------
1 | .responsive-media__item {
2 | aspect-ratio: var(--webui-media-aspect-ratio);
3 | block-size: auto;
4 | inline-size: 100%;
5 | }
6 |
--------------------------------------------------------------------------------
/ui/src/css/layout/index.css:
--------------------------------------------------------------------------------
1 | @import 'flex';
2 | @import 'grid';
3 | @import 'margin-padding';
4 | @import 'page';
5 | @import 'sidebar';
6 | @import 'stack';
7 | @import 'wrapper';
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # UI
2 | node_modules
3 | build
4 | dist/*
5 | public/*
6 | *.map
7 | *.log
8 |
9 | # Parcel bundler
10 | .parcel-cache
11 |
12 | # Storybook
13 | storybook-static
14 |
--------------------------------------------------------------------------------
/ui/.storybook/manager.js:
--------------------------------------------------------------------------------
1 | import { addons } from 'storybook/manager-api';
2 | import WebUIBoilerplate from "./theme";
3 |
4 | addons.setConfig({
5 | theme: WebUIBoilerplate,
6 | });
7 |
--------------------------------------------------------------------------------
/ui/public/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except these files
4 | !.gitignore
5 | !ajax/*
6 | !sprite.svg
7 | !storybook-overrides.css
8 | !theme-switcher.png
9 |
--------------------------------------------------------------------------------
/ui/src/css/_THEMES/theme1/fonts/roboto-medium/roboto-medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/basher/Web-UI-Boilerplate/HEAD/ui/src/css/_THEMES/theme1/fonts/roboto-medium/roboto-medium.woff
--------------------------------------------------------------------------------
/ui/src/css/_THEMES/theme1/fonts/roboto-regular/roboto-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/basher/Web-UI-Boilerplate/HEAD/ui/src/css/_THEMES/theme1/fonts/roboto-regular/roboto-regular.woff
--------------------------------------------------------------------------------
/ui/src/css/web-components/webui-fetch-html.css:
--------------------------------------------------------------------------------
1 | webui-fetch-html {
2 | &:not(:defined) {
3 | [data-fetch-trigger] {
4 | display: none;
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Ajax Loader/WebUIAjaxLoader.js:
--------------------------------------------------------------------------------
1 | export const WebUIAjaxLoaderHtml = () => /*html*/ `
2 |
3 | `;
4 |
--------------------------------------------------------------------------------
/ui/src/css/form/index.css:
--------------------------------------------------------------------------------
1 | @import 'checkbox';
2 | @import 'fieldset';
3 | @import 'form';
4 | @import 'input';
5 | @import 'label';
6 | @import 'radio';
7 | @import 'select';
8 | @import 'textarea';
9 |
--------------------------------------------------------------------------------
/ui/scripts/theme-config.js:
--------------------------------------------------------------------------------
1 | /*
2 | Valid themes for use with 'ui-theme.js' Node script.
3 | */
4 |
5 | var themes = (module.exports = {
6 | default: 'Whitelabel',
7 | theme1: 'Theme1'
8 | });
9 |
--------------------------------------------------------------------------------
/ui/types.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-var */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | declare global {
4 | var CommandEvent: any;
5 | var module: any;
6 | }
7 |
8 | export {};
9 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Radio/Radio.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as Radio from './Radio.stories';
3 |
4 |
5 |
6 | # Radio buttons
7 |
8 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Textarea/Textarea.js:
--------------------------------------------------------------------------------
1 | export const TextareaHtml = () => /*html*/ `
2 | Textarea label
3 |
7 | `;
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [ "./ui" ],
3 | "css.validate": false,
4 | "stylelint.enable": true,
5 | "stylelint.validate": [
6 | "css",
7 | "postcss",
8 | ],
9 | }
10 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Select/Select.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as Select from './Select.stories';
3 |
4 |
5 |
6 | # Select dropdown
7 |
8 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Checkbox/Checkbox.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as Checkbox from './Checkbox.stories';
3 |
4 |
5 |
6 | # Checkbox
7 |
8 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Textarea/Textarea.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as Textarea from './Textarea.stories';
3 |
4 |
5 |
6 | # Textarea
7 |
8 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Pagination/Pagination.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as Pagination from './Pagination.stories';
3 |
4 |
5 |
6 | # Pagination
7 |
8 |
--------------------------------------------------------------------------------
/ui/scripts/static-assets-config.js:
--------------------------------------------------------------------------------
1 | /*
2 | Valid static asset file extensions for use with 'static-assets-rename.js' Node script.
3 | */
4 |
5 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
6 | const validExt = (module.exports = ['.css', '.js', '.svg']);
7 |
--------------------------------------------------------------------------------
/ui/.storybook/theme/index.js:
--------------------------------------------------------------------------------
1 | import { create } from 'storybook/theming';
2 | import logo from './logo.png';
3 |
4 | export default create({
5 | base: 'light',
6 | brandTitle: 'A11Y Web UI Boilerplate',
7 | brandUrl: '/',
8 | brandImage: logo,
9 | });
10 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Checkbox/Checkbox.js:
--------------------------------------------------------------------------------
1 | export const CheckboxHtml = () => /*html*/ `
2 |
3 |
7 | Checkbox label
8 |
9 | `;
10 |
--------------------------------------------------------------------------------
/ui/src/css/web-components/webui-share.css:
--------------------------------------------------------------------------------
1 | webui-share {
2 | display: inline-block;
3 |
4 | &:not(:defined) {
5 | display: none;
6 | }
7 |
8 | [data-content] {
9 | --flex-gap: var(--webui-gutter-s);
10 |
11 | align-items: center;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ui/public/ajax/ajax.html:
--------------------------------------------------------------------------------
1 | Content Loaded via AJAX
2 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quos, cupiditate! Omnis fugiat, reprehenderit eveniet veritatis consequatur ducimus ea vero maxime vitae exercitationem repudiandae quasi Hello quisquam atque iusto aut nobis voluptas!
3 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Radio/Radio.stories.js:
--------------------------------------------------------------------------------
1 | import { RadioHtml } from './Radio';
2 |
3 | export default {
4 | title: 'Forms/Radio',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['forms'],
11 | };
12 |
13 | export const Radio = {
14 | render: () => RadioHtml(),
15 |
16 | };
17 |
--------------------------------------------------------------------------------
/ui/src/css/form/radio.css:
--------------------------------------------------------------------------------
1 | .radio {
2 | --flex-gap: var(--webui-gutter-s);
3 |
4 | [type='radio'] {
5 | block-size: 1.5em;
6 | inline-size: 1.5em;
7 |
8 | &:invalid:required:focus {
9 | box-shadow: 0 0 0 var(--webui-border-width-m)
10 | var(--webui-color-error-500);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Select/Select.stories.js:
--------------------------------------------------------------------------------
1 | import { SelectHtml } from './Select';
2 |
3 | export default {
4 | title: 'Forms/Select',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['forms'],
11 | };
12 |
13 | export const Select = {
14 | render: () => SelectHtml(),
15 | };
16 |
17 |
--------------------------------------------------------------------------------
/ui/stories/8. Pages/Homepage.stories.js:
--------------------------------------------------------------------------------
1 | import { HomepageHtml } from './Homepage';
2 |
3 | export default {
4 | title: 'Pages/Homepage',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['pages'],
11 | };
12 |
13 | export const Homepage = {
14 | render: () => HomepageHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/src/css/form/checkbox.css:
--------------------------------------------------------------------------------
1 | .checkbox {
2 | --flex-gap: var(--webui-gutter-s);
3 |
4 | [type='checkbox'] {
5 | block-size: 1.5em;
6 | inline-size: 1.5em;
7 |
8 | &:invalid:required:focus {
9 | box-shadow: 0 0 0 var(--webui-border-width-m)
10 | var(--webui-color-error-500);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Checkbox/Checkbox.stories.js:
--------------------------------------------------------------------------------
1 | import { CheckboxHtml } from './Checkbox';
2 |
3 | export default {
4 | title: 'Forms/Checkbox',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['forms'],
11 | };
12 |
13 | export const Checkbox = {
14 | render: () => CheckboxHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Textarea/Textarea.stories.js:
--------------------------------------------------------------------------------
1 | import { TextareaHtml } from './Textarea';
2 |
3 | export default {
4 | title: 'Forms/Textarea',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['forms'],
11 | };
12 |
13 | export const Textarea = {
14 | render: () => TextareaHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/src/javascript/utils/polyfills.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Import polyfills/ponyfills (for legacy browsers).
3 |
4 | Please refer to "browserSupportsAllFeatures()" function in "javascript/config/browser-supports-features.ts".
5 | */
6 |
7 | // Polyfill for Invoker Commands API.
8 | import 'invokers-polyfill';
9 |
10 | // Polyfill for scrollend event.
11 | import 'scrollyfills';
12 |
--------------------------------------------------------------------------------
/ui/stories/2. Foundations/Colours/Colours.stories.js:
--------------------------------------------------------------------------------
1 | import { ColoursHtml } from './Colours';
2 |
3 | export default {
4 | title: 'Foundations/Colours',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['foundations'],
11 | };
12 |
13 | export const Colours = {
14 | render: () => ColoursHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Skiplinks/Skiplinks.stories.js:
--------------------------------------------------------------------------------
1 | import { SkiplinksHtml } from './Skiplinks';
2 |
3 | export default {
4 | title: 'Components/Skiplinks',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['components'],
11 | };
12 |
13 | export const Skiplinks = {
14 | render: () => SkiplinksHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "allowSyntheticDefaultImports": true,
5 | "forceConsistentCasingInFileNames": true,
6 | "lib": ["ES2020", "DOM"],
7 | },
8 | "include": [
9 | "src/javascript/**/*",
10 | "types.d.ts",
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | ],
15 | }
16 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/DateOfBirth/DateOfBirth.stories.js:
--------------------------------------------------------------------------------
1 | import { DateOfBirthHtml } from './DateOfBirth';
2 |
3 | export default {
4 | title: 'Forms/Date Of Birth',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['forms'],
11 | };
12 |
13 | export const DateOfBirth = {
14 | render: () => DateOfBirthHtml(),
15 | };
16 |
17 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Select/Select.js:
--------------------------------------------------------------------------------
1 | export const SelectHtml = () => /*html*/ `
2 | Select label
3 |
7 | choose...
8 | option 1
9 | option 2
10 | option 3
11 |
12 | `;
13 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Breadcrumb/Breadcrumb.stories.js:
--------------------------------------------------------------------------------
1 | import { BreadcrumbHtml } from './Breadcrumb';
2 |
3 | export default {
4 | title: 'Components/Breadcrumb',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['components'],
11 | };
12 |
13 | export const Breadcrumb = {
14 | render: () => BreadcrumbHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Pagination/Pagination.stories.js:
--------------------------------------------------------------------------------
1 | import { PaginationHtml } from './Pagination';
2 |
3 | export default {
4 | title: 'Components/Pagination',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['components'],
11 | };
12 |
13 | export const Pagination = {
14 | render: () => PaginationHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/stories/2. Foundations/Typography/Typography.stories.js:
--------------------------------------------------------------------------------
1 | import { TypographyHtml } from './Typography';
2 |
3 | export default {
4 | title: 'Foundations/Typography',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['foundations'],
11 | };
12 |
13 | export const Typography = {
14 | render: () => TypographyHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/src/css/components/skiplinks.css:
--------------------------------------------------------------------------------
1 | .skiplinks {
2 | position: absolute;
3 |
4 | a {
5 | background-color: var(--webui-color-primary-500);
6 | color: var(--webui-color-neutral-300);
7 | display: inline-block;
8 | left: var(--webui-gutter-m);
9 | min-inline-size: 10vw;
10 | padding: var(--webui-gutter-s);
11 | text-align: center;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Image Gallery/ImageGallery.stories.js:
--------------------------------------------------------------------------------
1 | import { ImageGalleryHtml } from './ImageGallery';
2 |
3 | export default {
4 | title: 'Components/Image Gallery',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['components'],
11 | };
12 |
13 | export const ImageGallery = {
14 | render: () => ImageGalleryHtml(),
15 | };
16 |
--------------------------------------------------------------------------------
/ui/src/css/_THEMES/default/index.css:
--------------------------------------------------------------------------------
1 | /*
2 | ----------------------------------------------------------------------------
3 | NOTE:
4 | 1. Do NOT add any override styles here, as 'default' theme is actually what is already defined in 'css/base/' folder.
5 | 2. This file is here purely so that we can build and deploy to a 'build/css/default' folder.
6 | ----------------------------------------------------------------------------
7 | */
8 |
--------------------------------------------------------------------------------
/ui/src/css/components/search.css:
--------------------------------------------------------------------------------
1 | .search {
2 | :where(.form, .form__field) {
3 | align-items: center;
4 | flex-direction: row;
5 | }
6 |
7 | :where(.form__field, .input) {
8 | flex-grow: 1;
9 | }
10 |
11 | &:has(:valid:required, :invalid:required) .label {
12 | padding-inline-start: 0;
13 |
14 | &::after {
15 | content: '';
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Breadcrumb/Breadcrumb.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as Breadcrumb from './Breadcrumb.stories';
3 |
4 |
5 |
6 | # Breadcrumb
7 |
8 | ## Accessibility considerations
9 | - See the [ARIA APG breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/examples/breadcrumb/).
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ui/src/css/_THEMES/default/overrides/index.css:
--------------------------------------------------------------------------------
1 | /*
2 | ----------------------------------------------------------------------------
3 | NOTE:
4 | 1. Do NOT add any override styles here, as 'default' theme is actually what is already defined in 'css/base/' folder.
5 | 2. This file is here purely so that we can build and deploy to a 'build/css/default' folder.
6 | ----------------------------------------------------------------------------
7 | */
8 |
--------------------------------------------------------------------------------
/ui/src/css/web-components/index.css:
--------------------------------------------------------------------------------
1 | @import 'webui-ajax-loader';
2 | @import 'webui-carousel';
3 | @import 'webui-disclosure';
4 | @import 'webui-fetch-html';
5 | @import 'webui-make-clickable';
6 | @import 'webui-notify';
7 | @import 'webui-predictive-search';
8 | @import 'webui-prose';
9 | @import 'webui-range-input';
10 | @import 'webui-share';
11 | @import 'webui-tabs';
12 | @import 'webui-toggle';
13 | @import 'webui-video-player';
14 |
--------------------------------------------------------------------------------
/ui/src/css/global/icon.css:
--------------------------------------------------------------------------------
1 | .icon {
2 | aspect-ratio: 1;
3 | block-size: 1em; /* Scale icon to match font-size of parent element */
4 | fill: currentColor;
5 | }
6 |
7 | [data-icon-size='small'] {
8 | block-size: var(--webui-icon-size-s);
9 | }
10 |
11 | [data-icon-size='medium'] {
12 | block-size: var(--webui-icon-size-m);
13 | }
14 |
15 | [data-icon-size='large'] {
16 | block-size: var(--webui-icon-size-l);
17 | }
18 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/DateOfBirth/DateOfBirth.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as DateOfBirth from './DateOfBirth.stories';
3 |
4 |
5 |
6 | # Date of birth
7 |
8 | ## Usability considerations
9 | - All 3 form fields make use of [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attributes.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ui/src/css/components/index.css:
--------------------------------------------------------------------------------
1 | @import 'accordion';
2 | @import 'ajax';
3 | @import 'banner';
4 | @import 'breadcrumb';
5 | @import 'button';
6 | @import 'button-group';
7 | @import 'card';
8 | @import 'carousel';
9 | @import 'image';
10 | @import 'image-gallery';
11 | @import 'modal';
12 | @import 'nav';
13 | @import 'pagination';
14 | @import 'responsive-media';
15 | @import 'search';
16 | @import 'skin';
17 | @import 'skiplinks';
18 | @import 'tables';
19 |
--------------------------------------------------------------------------------
/ui/src/css/components/tables.css:
--------------------------------------------------------------------------------
1 | table,
2 | .table {
3 | :where(th, td) {
4 | border: var(--webui-border-width-s) solid;
5 | padding: var(--webui-gutter-m);
6 | }
7 |
8 | th {
9 | font-weight: bold;
10 | }
11 |
12 | caption {
13 | margin-block-end: var(--webui-gutter-xs);
14 | }
15 | }
16 |
17 | /* Responsive container. */
18 | .table-container[role='region'][tabindex] {
19 | overflow: auto;
20 | }
21 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Buttons & Links/Button Group/ButtonGroup.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
2 | import * as ButtonGroup from './ButtonGroup.stories';
3 |
4 |
5 |
6 | ## Button group
7 |
8 |
9 |
10 | ## Link group
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Tables/Table.stories.js:
--------------------------------------------------------------------------------
1 | import { TableHtml, ResponsiveTableHtml } from './Table';
2 |
3 | export default {
4 | title: 'Components/Tables',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['components'],
11 | };
12 |
13 | export const Table = {
14 | render: () => TableHtml(),
15 | };
16 |
17 | export const ResponsiveTable = {
18 | render: () => ResponsiveTableHtml(),
19 | };
20 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Prose/WebUIProse.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIProseHtml } from './WebUIProse';
2 | export default {
3 | title: 'Web Components Or Custom Elements/',
4 | parameters: {
5 | status: {
6 | type: 'stable',
7 | },
8 | },
9 | tags: ['web components'],
10 | };
11 |
12 | export const WebUIProse = {
13 | render: () => WebUIProseHtml(),
14 | };
15 | WebUIProse.storyName = '';
16 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Modal/WebUIModal.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIModalHtml } from './WebUIModal';
2 |
3 | export default {
4 | title: 'Web Components Or Custom Elements/',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['web components'],
11 | };
12 |
13 | export const WebUIModal = {
14 | render: () => WebUIModalHtml(),
15 | };
16 | WebUIModal.storyName = '';
17 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Input/Input.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as Input from './Input.stories';
3 |
4 |
5 |
6 | ## Text input
7 |
8 |
9 | ## Email input
10 |
11 |
12 | ## Telephone input
13 | - Please note that the REGEX `pattern` is not accurate enough for production use. In reality, use the same pattern as the back-end validation.
14 |
15 |
16 |
--------------------------------------------------------------------------------
/ui/stories/3. Layout/Sidebar/Sidebar.stories.js:
--------------------------------------------------------------------------------
1 | import { SidebarHtml, SidebarRightHtml } from './Sidebar';
2 |
3 | export default {
4 | title: 'Layout/Sidebar',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['layout'],
11 | };
12 |
13 | export const Sidebar = {
14 | render: () => SidebarHtml(),
15 | };
16 |
17 | export const SidebarRight = {
18 | render: () => SidebarRightHtml(),
19 | };
20 | SidebarRight.storyName = 'Right Sidebar';
21 |
--------------------------------------------------------------------------------
/ui/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ui/src/css/web-components/webui-video-player.css:
--------------------------------------------------------------------------------
1 | webui-video-player {
2 | [command='show-modal'] {
3 | display: grid;
4 | grid-template-areas: 'video';
5 | inline-size: fit-content;
6 | padding: unset;
7 | place-items: center;
8 |
9 | > * {
10 | grid-area: video;
11 | }
12 |
13 | .icon {
14 | block-size: var(--webui-icon-size-l);
15 | }
16 | }
17 |
18 | dialog {
19 | inline-size: 100%;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ui/src/css/components/pagination.css:
--------------------------------------------------------------------------------
1 | .pagination {
2 | [aria-current] {
3 | background-color: var(--webui-color-primary-500);
4 | border-color: light-dark(
5 | var(--webui-color-primary-500),
6 | var(--webui-color-neutral-100)
7 | );
8 | color: var(--webui-color-neutral-100);
9 | font-weight: bold;
10 | }
11 | }
12 |
13 | .pagination__list {
14 | --flex-gap: var(--webui-gutter-xs);
15 |
16 | align-items: center;
17 | justify-content: center;
18 | }
19 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Breadcrumb/Breadcrumb.js:
--------------------------------------------------------------------------------
1 | export const BreadcrumbHtml = () => /*html*/ `
2 |
3 |
4 |
5 | Home
6 |
7 |
8 | Parent
9 |
10 |
11 | Parent
12 |
13 |
14 | Current page
15 |
16 |
17 |
18 | `;
19 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIFetchHtmlHtml } from './WebUIFetchHtml';
2 |
3 | export default {
4 | title: 'Web Components Or Custom Elements/',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['web components'],
11 | };
12 |
13 | export const WebUIFetchHtml = {
14 | render: () => WebUIFetchHtmlHtml(),
15 | };
16 | WebUIFetchHtml.storyname = '';
17 |
--------------------------------------------------------------------------------
/ui/src/javascript/templates/search-results.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @param {object} data - Array of matched search terms
3 | * @returns {string}
4 | * */
5 |
6 | const template = (data: []): string => {
7 | const resultTemplate = data.map((result: Record) => {
8 | return `
9 |
10 | ${result.name}
11 |
12 | `;
13 | });
14 |
15 | return `${resultTemplate.join('')}`;
16 | };
17 |
18 | export default template;
19 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Ajax Loader/WebUIAjaxLoader.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIAjaxLoaderHtml } from './WebUIAjaxLoader';
2 |
3 | export default {
4 | title: 'Web Components Or Custom Elements/',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['web components'],
11 | };
12 |
13 | export const WebUIAjaxLoader = {
14 | render: () => WebUIAjaxLoaderHtml(),
15 | };
16 | WebUIAjaxLoader.storyName = '';
17 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Video Player/WebUIVideoPlayer.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIVideoPlayerHtml } from './WebUIVideoPlayer';
2 | export default {
3 | title: 'Web Components Or Custom Elements/',
4 | parameters: {
5 | status: {
6 | type: 'stable',
7 | },
8 | },
9 | tags: ['web components'],
10 | };
11 |
12 | export const WebUIVideoPlayer = {
13 | render: () => WebUIVideoPlayerHtml(),
14 | };
15 | WebUIVideoPlayer.storyName = '';
16 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Responsive Media/ResponsiveMedia.stories.js:
--------------------------------------------------------------------------------
1 | import { ResponsiveIframeHtml, ResponsiveVideoHtml } from './ResponsiveMedia';
2 | export default {
3 | title: 'Components/Responsive Media',
4 | parameters: {
5 | status: {
6 | type: 'stable',
7 | },
8 | },
9 | tags: ['components'],
10 | };
11 |
12 | export const ResponsiveIframe = {
13 | render: () => ResponsiveIframeHtml(),
14 | };
15 |
16 | export const ResponsiveVideo = {
17 | render: () => ResponsiveVideoHtml(),
18 | };
19 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Ajax Loader/WebUIAjaxLoader.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as WebUIAjaxLoader from './WebUIAjaxLoader.stories';
3 |
4 |
5 |
6 | # ``
7 | - Renders an animated loader via JavaScript while Ajax requests are being handled.
8 |
9 |
10 |
11 | ## Accessibility considerations
12 | - The Ajax loading spinner is an `` with an accessible `loading` name.
13 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Range Input/WebUIRangeInput.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIRangeInputHtml } from './WebUIRangeInput';
2 |
3 | export default {
4 | title: 'Web Components Or Custom Elements/',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['web components'],
11 | };
12 |
13 | export const WebUIRangeInput = {
14 | render: (args) => WebUIRangeInputHtml(args),
15 | };
16 | WebUIRangeInput.storyName = '';
17 |
--------------------------------------------------------------------------------
/ui/src/css/utilities/layout.css:
--------------------------------------------------------------------------------
1 | /*
2 | ----------------------------------------------------------------------------
3 | Center content.
4 | ----------------------------------------------------------------------------
5 | */
6 | .u-center {
7 | text-align: center;
8 |
9 | /* Setting auto margins on direct children without a CSS classname ensures that typographic elements whose max width is constrained by "--webui-type-max-line-length" will be correctly centered. */
10 | > :not([class]) {
11 | margin-inline: auto;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Form Validate/WebUIFormValidate.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIFormValidateHtml } from './WebUIFormValidate';
2 |
3 | export default {
4 | title: 'Web Components Or Custom Elements/',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['web components'],
11 | };
12 |
13 | export const WebUIFormValidate = {
14 | render: () => WebUIFormValidateHtml(),
15 | };
16 | WebUIFormValidate.storyName = '';
17 |
--------------------------------------------------------------------------------
/ui/stories/3. Layout/MarginPadding/MarginPadding.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
2 | import * as MarginPadding from './MarginPadding.stories';
3 |
4 |
5 |
6 | # Margin and padding
7 | - Applies `m-block`|`m-inline` and `p-block`|`p-inline` override classes.
8 | - Uses the [Utopia fluid space calculator](https://utopia.fyi/) to output the **minimum**, **maximum** and **ideal** gutter sizes.
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Make Clickable/WebUIMakeClickable.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIMakeClickableHtml } from './WebUIMakeClickable';
2 |
3 | export default {
4 | title: 'Web Components Or Custom Elements/',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['web components'],
11 | };
12 |
13 | export const WebUIMakeClickable = {
14 | render: () => WebUIMakeClickableHtml(),
15 | };
16 | WebUIMakeClickable.storyName = '';
17 |
--------------------------------------------------------------------------------
/ui/src/css/layout/wrapper.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | --wrapper-max-width: 64rem;
3 |
4 | margin-inline: auto;
5 | max-inline-size: var(--wrapper-max-width);
6 | }
7 |
8 | [data-wrapper='small'] {
9 | --wrapper-max-width: 48rem;
10 | }
11 |
12 | [data-wrapper='large'] {
13 | --wrapper-max-width: 75rem;
14 | }
15 |
16 | [data-wrapper='fullbleed'] {
17 | --wrapper-max-width: 100vw; /* 100dvi might have performance implications */
18 | }
19 |
20 | [data-wrapper='fit-content'] {
21 | --wrapper-max-width: revert;
22 |
23 | inline-size: fit-content;
24 | }
25 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Prose/WebUIProse.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as WebUIProse from './WebUIProse.stories';
3 |
4 |
5 |
6 | # ``
7 | - Manages prose content (e.g. from a CMS editor).
8 | - The spacing between the individual typographic elements is handled by the [stack layout](/docs/layout-stack--docs).
9 | - Tables have a `` programatically added via JavaScript to make them responsive.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ui/src/css/layout/page.css:
--------------------------------------------------------------------------------
1 | .page {
2 | display: grid;
3 | grid-template-rows: auto 1fr auto;
4 | margin-inline: auto;
5 | max-inline-size: var(--webui-site-max-width);
6 | min-block-size: 100vh; /* 100dvb might have performance implications */
7 |
8 | /* Main is a grid item, so this affects overflow behaviour on components like tables. */
9 | .main {
10 | overflow-x: hidden;
11 | }
12 | }
13 |
14 | .page__sidebar {
15 | .sidebar {
16 | /* Set explicit flex-basis (width) for page sidebar. */
17 | --sidebar-width: 20rem;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ui/stories/3. Layout/Stack/Stack.js:
--------------------------------------------------------------------------------
1 | const style = /*html*/ `
2 |
8 | `;
9 |
10 | export const StackHtml = (args) => /*html*/ `
11 | ${style}
12 |
16 |
Child item of "stack" parent...
17 |
Child item of "stack" parent...
18 |
Child item of "stack" parent...
19 |
20 | `;
21 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Cards/CardsNoArgs.stories.js:
--------------------------------------------------------------------------------
1 | import { CardGridHtml, CardCarouselHtml } from './Cards';
2 |
3 | export default {
4 | title: 'Components/Cards',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['components'],
11 | };
12 |
13 | export const CardGrid = {
14 | render: (args) => CardGridHtml(args),
15 | };
16 | CardGrid.storyName = 'Cards In A Grid';
17 |
18 | export const CardCarousel = {
19 | render: (args) => CardCarouselHtml(args),
20 | };
21 | CardCarousel.storyName = 'Cards In A Carousel';
22 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Predictive Search/WebUIPredictiveSearch.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIPredictiveSearchHtml } from './WebUIPredictiveSearch';
2 |
3 | export default {
4 | title: 'Web Components Or Custom Elements/
',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['web components'],
11 | };
12 |
13 | export const WebUIPredictiveSearch = {
14 | render: () => WebUIPredictiveSearchHtml(),
15 | };
16 | WebUIPredictiveSearch.storyName = '';
17 |
--------------------------------------------------------------------------------
/ui/scripts/bundle-all-themes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Bundles all themes for use by Storybook.
3 | *
4 | * @return {void}
5 | */
6 |
7 | const { exec } = require('node:child_process');
8 | const themes = require('./theme-config');
9 |
10 | let strNpm = '';
11 | for (const [key] of Object.entries(themes)) {
12 | strNpm += `npm run build:theme --theme=${key} && `;
13 | console.log(`Building UI theme: ${key}`);
14 | }
15 |
16 | strNpm = strNpm.substring(0, strNpm.lastIndexOf('&&') - 1);
17 |
18 | // Console logging 'stdout' shows postbuild script logs.
19 | exec(strNpm, (error, stdout, stderr) => console.log(`STDOUT: ${stdout}`));
20 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Skin/Skin.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
2 | import * as Skin from './Skin.stories';
3 |
4 |
5 |
6 | # Skin
7 | - Adds a *"skin"* or theming layer to components. For example, background and foreground colours, borders, etc.
8 | - Ideally, there should be **NO layout styles** applied to skin component (e.g. widths, margins). Use layout components for this.
9 |
10 |
11 |
12 |
13 | ## Skin applied to wrapper layout
14 |
15 |
16 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Tables/Table.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as Table from './Table.stories';
3 |
4 |
5 |
6 | # Tables
7 |
8 |
9 | ## Responsive table
10 |
11 | ### Accessibility and responsive behaviour considerations
12 | - Placing a [table inside an overflowing container](https://adrianroselli.com/2020/11/under-engineered-responsive-tables.html?ref=sidebar) is the simplest way to achieve a fully accessible & responsive table, which is keyboard & assistive technology friendly.
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Images/Images.stories.js:
--------------------------------------------------------------------------------
1 | import {
2 | ResponsiveImageHtml,
3 | FixedSizeImageHtml
4 | } from './Images';
5 |
6 | export default {
7 | title: 'Components/Images',
8 | parameters: {
9 | status: {
10 | type: 'stable',
11 | },
12 | },
13 | tags: ['components'],
14 | };
15 |
16 | export const ResponsiveImage = {
17 | render: () => ResponsiveImageHtml(),
18 | };
19 | ResponsiveImage.storyName = 'Responsive Images';
20 |
21 | export const FixedSizeImage = {
22 | render: () => FixedSizeImageHtml(),
23 | };
24 | FixedSizeImage.storyName = 'Fixed Size Images';
25 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Banners/Banners.stories.js:
--------------------------------------------------------------------------------
1 | import {
2 | FullwidthBannerHtml,
3 | FullwidthBannerOverlayHtml
4 | } from './Banners';
5 |
6 | export default {
7 | title: 'Components/Banners',
8 | parameters: {
9 | status: {
10 | type: 'stable',
11 | },
12 | },
13 | tags: ['components'],
14 | };
15 |
16 | export const FullwidthBanner = {
17 | render: () => FullwidthBannerHtml(),
18 | };
19 |
20 | export const FullwidthBannerOverlay = {
21 | render: () => FullwidthBannerOverlayHtml(),
22 | };
23 | FullwidthBannerOverlay.storyName = 'Fullwidth Banner With Image Overlay Mask';
24 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Radio/Radio.js:
--------------------------------------------------------------------------------
1 | export const RadioHtml = () => /*html*/ `
2 |
3 | Radio legend
4 |
5 |
10 | Radio 1 label
11 |
12 |
13 |
18 | Radio 2 label
19 |
20 |
21 | `;
22 |
--------------------------------------------------------------------------------
/ui/src/javascript/web-components/webui-notify.ts:
--------------------------------------------------------------------------------
1 | class WebUINotify extends HTMLElement {
2 | private btnClose: HTMLButtonElement | null;
3 |
4 | constructor() {
5 | super();
6 | this.btnClose = this.querySelector('[data-close]');
7 |
8 | if (!this.btnClose) return;
9 |
10 | this.btnClose.addEventListener('click', this);
11 | }
12 |
13 | /**
14 | * @description Handle constructor() event listeners.
15 | */
16 | public handleEvent(): void {
17 | this.setAttribute('hidden', '');
18 | }
19 | }
20 |
21 | customElements.define('webui-notify', WebUINotify);
22 | export default WebUINotify;
23 |
--------------------------------------------------------------------------------
/ui/src/css/_THEMES/theme1/tokens.css:
--------------------------------------------------------------------------------
1 | /*
2 | ----------------------------------------------------------------------------
3 | "Primitive" design tokens for dynamic theming.
4 | ----------------------------------------------------------------------------
5 | */
6 | :root {
7 | --color-brand: oklch(49.33% 0.0892 126.19); /* darkolivegreen */
8 | --color-accent-green: oklch(75.14% 0.0892 142.55);
9 | --color-accent-red: oklch(48.29% 0.0892 26.34);
10 | --color-accent-yellow: oklch(82.25% 0.0892 96.1742);
11 | --color-accent-blue: oklch(71.69% 0.0892 226.5552);
12 | --font-family: robotomedium;
13 | --font-family-title: robotoregular;
14 | }
15 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Tabs/WebUITabs.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUITabsHtml, WebUIVerticalTabsHtml } from './WebUITabs';
2 | export default {
3 | title: 'Web Components Or Custom Elements/',
4 | parameters: {
5 | status: {
6 | type: 'stable',
7 | },
8 | },
9 | tags: ['web components'],
10 | };
11 |
12 | export const WebUITabs = {
13 | render: () => WebUITabsHtml(),
14 | };
15 | WebUITabs.storyName = '';
16 |
17 | export const WebUIVerticalTabs = {
18 | render: () => WebUIVerticalTabsHtml(),
19 | };
20 | WebUIVerticalTabs.storyName = ' Vertical Tabs';
21 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Image Gallery/ImageGallery.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as ImageGallery from './ImageGallery.stories';
3 |
4 |
5 |
6 | # Image gallery
7 | - This component uses the [modal](/docs/components-modal--docs) component to create a simple "lightbox".
8 | - The image thumbnails are arranged with a simple [grid list layout](/story/layout-grid--grid-list).
9 |
10 | > Please note: For brevity, the large images only have a subset of the [responsive image](/docs/components-images--docs) `` markup in the `` element.
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ui/stories/3. Layout/Page/Page.stories.js:
--------------------------------------------------------------------------------
1 | import { PageHtml, PageSidebarHtml, PageSidebarRightHtml } from './Page';
2 |
3 | export default {
4 | title: 'Layout/Page',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['layout'],
11 | };
12 |
13 | export const Page = {
14 | render: () => PageHtml(),
15 | };
16 |
17 | export const PageSidebar = {
18 | render: () => PageSidebarHtml(),
19 | };
20 | PageSidebar.storyName = 'Page With Sidebar';
21 |
22 | export const PageSidebarRight = {
23 | render: () => PageSidebarRightHtml(),
24 | };
25 | PageSidebarRight.storyName = 'Page With Right Sidebar';
26 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Toggle/WebUIToggle.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as WebUIToggle from './WebUIToggle.stories';
3 |
4 |
5 |
6 | # ``
7 | - Toggle button (or switch)
8 |
9 |
10 |
11 | ## Accessibility considerations
12 | - See the [ARIA APG switch pattern](https://www.w3.org/WAI/ARIA/apg/patterns/switch/examples/switch-button/) and [Inclusive Components toggle](https://inclusive-components.design/toggle-button/) examples.
13 |
14 | ## Toggle with visible label
15 |
16 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Range Input/WebUIRangeInput.js:
--------------------------------------------------------------------------------
1 | export const WebUIRangeInputHtml = () => /*html*/ `
2 |
3 |
4 |
5 | Range slider
6 | Min 0, max 100
7 |
8 |
17 |
18 |
19 |
20 | `;
21 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Share/WebUIShare.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIShareHtml, WebUIShareAnotherUrlHtml } from './WebUIShare';
2 |
3 | export default {
4 | title: 'Web Components Or Custom Elements/',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['web components'],
11 | };
12 |
13 | export const WebUIShare = {
14 | render: () => WebUIShareHtml(),
15 | };
16 | WebUIShare.storyName = '';
17 |
18 | export const WebUIShareAnotherUrl = {
19 | render: () => WebUIShareAnotherUrlHtml(),
20 | };
21 | WebUIShareAnotherUrl.storyName = ' Another URL';
22 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.js:
--------------------------------------------------------------------------------
1 | export const WebUIFetchHtmlHtml = () => /*html*/ `
2 | This is simply a demo to show the core features of a component that has some Ajax dependencies (e.g. fetching an HTML fragment).
3 |
4 |
5 |
11 | Fetch HTML
12 |
13 |
14 |
19 |
20 | `;
21 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Toggle/WebUIToggle.stories.js:
--------------------------------------------------------------------------------
1 | import { WebUIToggleHtml, WebUIToggleWithLabelHtml } from './WebUIToggle';
2 | export default {
3 | title: 'Web Components Or Custom Elements/',
4 | parameters: {
5 | status: {
6 | type: 'stable',
7 | },
8 | },
9 | tags: ['web components'],
10 | };
11 |
12 | export const WebUIToggle = {
13 | render: () => WebUIToggleHtml(),
14 | };
15 | WebUIToggle.storyName = '';
16 |
17 | export const WebUIToggleWithLabel = {
18 | render: () => WebUIToggleWithLabelHtml(),
19 | };
20 | WebUIToggleWithLabel.storyName = ' With Visible Label';
21 |
--------------------------------------------------------------------------------
/ui/stories/7. Utilities/Utilities.stories.js:
--------------------------------------------------------------------------------
1 | import {
2 | ScreenReaderHtml,
3 | HideContentHtml,
4 | CenterContentHtml
5 | } from './Utilities';
6 |
7 | export default {
8 | title: 'Utilities/Helpers and Utilities',
9 | parameters: {
10 | status: {
11 | type: 'stable',
12 | },
13 | },
14 | tags: ['utilities'],
15 | };
16 |
17 | export const ScreenReader = {
18 | render: () => ScreenReaderHtml(),
19 | };
20 | ScreenReader.storyName = 'Screen Reader Only Content';
21 |
22 | export const HideContent = {
23 | render: () => HideContentHtml(),
24 | };
25 |
26 | export const CenterContent = {
27 | render: () => CenterContentHtml(),
28 | };
29 |
--------------------------------------------------------------------------------
/ui/src/javascript/index.ts:
--------------------------------------------------------------------------------
1 | // Import Sass entry file and SVG sprite.
2 | import '../../src/css/index.css';
3 | import '../../src/images/sprite.svg';
4 |
5 | // Import config and UI module initialisation.
6 | import { browserSupportsAllFeatures } from './config/browser-supports-features';
7 | import { uiInit } from './ui-init';
8 |
9 | if (browserSupportsAllFeatures()) {
10 | uiInit();
11 | } else {
12 | // Dynamic import polyfills, then instantiate UI modules.
13 | (async (): Promise => {
14 | try {
15 | await import('./utils/polyfills');
16 | uiInit();
17 | } catch (error) {
18 | console.error(error);
19 | }
20 | })();
21 | }
22 |
--------------------------------------------------------------------------------
/ui/src/css/components/breadcrumb.css:
--------------------------------------------------------------------------------
1 | .breadcrumb {
2 | [aria-current] {
3 | color: light-dark(
4 | var(--webui-color-primary-500),
5 | var(--webui-color-neutral-100)
6 | );
7 | font-weight: bold;
8 | }
9 |
10 | li {
11 | position: relative;
12 |
13 | &:not(:first-child)::before {
14 | content: '>';
15 | display: inline-block;
16 | inline-size: 1ch;
17 | inset-inline-start: -1em;
18 | position: absolute;
19 | }
20 | }
21 |
22 | a {
23 | color: inherit;
24 |
25 | &:hover {
26 | text-decoration: none;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ui/src/css/web-components/webui-prose.css:
--------------------------------------------------------------------------------
1 | /*
2 | ----------------------------------------------------------------------------
3 | Prose content (e.g. from CMS editor).
4 | ----------------------------------------------------------------------------
5 | */
6 | webui-prose {
7 | overflow-x: hidden; /* Just in case content is stupidly wide */
8 |
9 | /* Uses stack layout to handle spacing between prose elements, based on their rendered font size. */
10 | > [class*='stack'] {
11 | --stack-gutter: 1em;
12 |
13 | /* Remove spacing for adjacent headings. */
14 | :where(h1 + h2, h2 + h3, h3 + h4, h4 + h5, h5 + h6) {
15 | --stack-gutter: 0;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Input/Input.stories.js:
--------------------------------------------------------------------------------
1 | import {
2 | InputTextHtml,
3 | InputEmailHtml,
4 | InputTelHtml
5 | } from './Input';
6 |
7 | export default {
8 | title: 'Forms/Input',
9 | parameters: {
10 | status: {
11 | type: 'stable',
12 | },
13 | },
14 | tags: ['forms'],
15 | };
16 |
17 | export const InputText = {
18 | render: () => InputTextHtml(),
19 | };
20 | InputText.storyName = 'Text Input';
21 |
22 | export const InputEmail = {
23 | render: () => InputEmailHtml(),
24 | };
25 | InputEmail.storyName = 'Email Input';
26 |
27 | export const InputTel = {
28 | render: () => InputTelHtml(),
29 | };
30 | InputTel.storyName = 'Telephone Input';
31 |
--------------------------------------------------------------------------------
/ui/src/css/_THEMES/theme1/index.css:
--------------------------------------------------------------------------------
1 | /*
2 | ----------------------------------------------------------------------------
3 | THEME overrides.
4 | ----------------------------------------------------------------------------
5 | */
6 |
7 | /*
8 | ----------------------------------------------------------------------------
9 | Override "primitive" CSS design tokens.
10 | ----------------------------------------------------------------------------
11 | */
12 | @import 'tokens' screen;
13 |
14 | /*
15 | ----------------------------------------------------------------------------
16 | Other overrides.
17 | ----------------------------------------------------------------------------
18 | */
19 | @import 'overrides/index' screen;
20 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Disclosure/WebUIDisclosure.js:
--------------------------------------------------------------------------------
1 | export const WebUIDisclosureHtml = (args) => /*html*/ `
2 |
6 |
13 | Show / Hide
14 |
15 |
16 |
17 |
Content to be shown/hidden. Use this component when accordion or tabs components cannot be used.
18 |
19 |
20 | `;
21 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Skin/Skin.js:
--------------------------------------------------------------------------------
1 | export const SkinHtml = (args) => /*html*/ `
2 |
3 |
Skin component allows us to add a "skin" or theming layer to components. For example:
4 |
5 | Background & foreground colours
6 | Borders & shadows
7 | Padding
8 | Text alignment
9 |
10 |
11 | `;
12 |
13 | export const SkinWrapperHtml = (args) => /*html*/ `
14 |
15 |
Skin component also allows us to add a "skin" or theming layer to wrapper component.
16 |
Wrapper (default)
17 |
18 | `;
19 |
--------------------------------------------------------------------------------
/ui/src/css/layout/sidebar.css:
--------------------------------------------------------------------------------
1 | /*
2 | ----------------------------------------------------------------------------
3 | Sidebar.
4 |
5 | See https://every-layout.dev/layouts/sidebar/ for explanation of Flex behaviour.
6 | ----------------------------------------------------------------------------
7 | */
8 | .sidebar {
9 | --sidebar-width: revert;
10 |
11 | flex-basis: var(--sidebar-width);
12 | flex-grow: 1;
13 | }
14 |
15 | .sidebar-container {
16 | display: flex;
17 | flex-wrap: wrap;
18 | gap: var(--webui-gutter-m);
19 |
20 | :not(.sidebar) {
21 | --content-min-width: 50%;
22 |
23 | flex-basis: 0;
24 | flex-grow: 999;
25 | min-inline-size: var(--content-min-width);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Skiplinks/Skiplinks.js:
--------------------------------------------------------------------------------
1 | export const SkiplinksHtml = () => /*html*/ `
2 |
3 | Skip to main content
4 |
5 |
6 | Some content preceding skiplink target - Lorem ipsum, dolor sit amet consectetur adipisicing elit. Accusamus, incidunt non numquam deserunt dolorem, perspiciatis error asperiores fuga.
7 |
8 |
9 | This is the main landmark.
10 |
11 | It is a skiplink target.
12 | It has tabindex="-1" to ensure it can receive focus.
13 | See the docs for more info.
14 |
15 |
16 | `;
17 |
--------------------------------------------------------------------------------
/ui/stories/5. Components/Accordion/Accordion.stories.js:
--------------------------------------------------------------------------------
1 | import { AccordionHtml, AccordionExclusiveHtml, AccordionCustomIconHtml } from './Accordion';
2 | export default {
3 | title: 'Components/Accordion',
4 | parameters: {
5 | status: {
6 | type: 'stable',
7 | },
8 | },
9 | tags: ['components'],
10 | };
11 |
12 | export const Accordion = {
13 | render: () => AccordionHtml(),
14 | };
15 |
16 | export const AccordionExclusive = {
17 | render: () => AccordionExclusiveHtml(),
18 | };
19 | AccordionExclusive.storyName = 'Exclusive Accordion';
20 |
21 | export const AccordionCustomIcon = {
22 | render: () => AccordionCustomIconHtml(),
23 | };
24 | AccordionCustomIcon.storyName = 'Accordion With Custom Icon';
25 |
--------------------------------------------------------------------------------
/ui/stories/4. Forms/Form/FormNoArgs.stories.js:
--------------------------------------------------------------------------------
1 | import { FormServerValidationHtml } from './Form';
2 |
3 | export default {
4 | title: 'Forms/Form',
5 | parameters: {
6 | status: {
7 | type: 'stable',
8 | },
9 | },
10 | tags: ['forms'],
11 | };
12 |
13 | export const FormServerValidation = {
14 | render: () => FormServerValidationHtml(),
15 | };
16 | FormServerValidation.storyName = 'Form (Server Validation)';
17 |
18 | export const WebUIFormValidate = {
19 | render: () => /*html*/ `
20 |
21 | See the
22 | <webui-form-validate>
23 | Web Component.
24 |
25 | `
26 | };
27 | WebUIFormValidate.storyName = ' JavaScript Validation';
28 |
--------------------------------------------------------------------------------
/ui/stories/6. Web Components Or Custom Elements/WebUI Video Player/WebUIVideoPlayer.mdx:
--------------------------------------------------------------------------------
1 | import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2 | import * as WebUIVideoPlayer from './WebUIVideoPlayer.stories';
3 |
4 |
5 |
6 | # ``
7 | - This Web Component uses a native [modal](/docs/components-modal--docs) component to create a simple "lightbox".
8 |
9 |
10 |
11 | ## Responsive, accessibility and performance considerations
12 | - See the [responsive media/video](/story/components-responsive-media--responsive-video) component.
13 | - If the browser does not support native `