diff --git a/.eslintrc.yml b/.eslintrc.yml index 212fe95a..a985a616 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,26 +1,33 @@ root: true extends: - airbnb +plugins: + - import-newlines env: - es6: false + es6: true browser: true + jest: true globals: BACKGROUNDS: true ICONS: true LOGOS: true SCSS_VARIABLES: true Set: true + dom: true + assertUniqueCssClasses: true rules: - strict: 0 + func-names: 0 + guard-for-in: 0 + import-newlines/enforce: [2, { "items": 100, "forceSingleLine": true, "max-len": 10000, "maxLen": 10000 }] import/no-extraneous-dependencies: 0 import/prefer-default-export: 0 - no-restricted-syntax: 0 - guard-for-in: 0 - quotes: [ 2, "single", { "avoidEscape": true, "allowTemplateLiterals": true } ] + max-len: ["error", { "code": 10000, "comments": 80 }] no-continue: 0 - no-plusplus: 0 - no-param-reassign: 0 - no-new: 0 no-nested-ternary: 0 - func-names: 0 - max-len: ["error", { "code": 10000, "comments": 80 }] + no-new: 0 + no-param-reassign: 0 + no-plusplus: 0 + no-restricted-syntax: 0 + object-curly-newline: [2, { "ImportDeclaration": "never"}] + quotes: [ 2, "single", { "avoidEscape": true, "allowTemplateLiterals": true } ] + strict: 0 diff --git a/.gitattributes b/.gitattributes index 2a573f3b..9224ca21 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ +/**/*.test.* export-ignore /.editorconfig export-ignore /.eslintignore export-ignore /.eslintrc.yml export-ignore @@ -8,10 +9,9 @@ /.storybook export-ignore /.stylelintignore export-ignore /.stylelintrc.json export-ignore +/.twig-cs-fixer.php export-ignore +/composer.json export-ignore +/jest.config.js export-ignore /renovate.json export-ignore +/tests export-ignore /webpack export-ignore - -# Exclude demo assets as they are erroneously located in dist assets. -# @see https://github.com/civictheme/uikit/issues/30 -/assets/images export-ignore -/assets/videos export-ignore diff --git a/.github/ISSUE_TEMPLATE/DEFECT-REPORT.yml b/.github/ISSUE_TEMPLATE/DEFECT-REPORT.yml new file mode 100644 index 00000000..a6b4f1ea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/DEFECT-REPORT.yml @@ -0,0 +1,32 @@ +name: Defect Report +description: File a defect report +labels: ["Type: Defect"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this defect report. + - type: textarea + id: summary + attributes: + label: Summary + validations: + required: true + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to reproduce + validations: + required: true + - type: textarea + id: observed-outcome + attributes: + label: Observed outcome + validations: + required: true + - type: textarea + id: expected-outcome + attributes: + label: Expected outcome + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml new file mode 100644 index 00000000..560baa4f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml @@ -0,0 +1,34 @@ +name: Feature Request +description: Suggest a new feature +labels: ["Type: Feature"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request. + - type: textarea + id: user-story + attributes: + label: User story + value: | + As a + I want + So that + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + validations: + required: true + - type: textarea + id: acceptance-criteria + attributes: + label: Acceptance criteria + value: | + Given I am a + When I + Then I + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..901cb812 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: 💬 Drush Slack + url: https://drupal.slack.com/archives/C039UV0CQBZ + about: Join the CivicTheme Community to talk, exchange experiences or ask and answer questions. + - name: 💧 Drupal.org + url: https://www.drupal.org/project/civictheme + about: Submit an issue for Drupal theme diff --git a/.github/workflows/post-opened-issue-to-jira.yml b/.github/workflows/post-opened-issue-to-jira.yml new file mode 100644 index 00000000..d9b47264 --- /dev/null +++ b/.github/workflows/post-opened-issue-to-jira.yml @@ -0,0 +1,45 @@ +name: Post opened issue to Jira + +on: + issues: + types: + - opened + +jobs: + issue_opened: + runs-on: ubuntu-latest + + steps: + - name: Create issue body file + run: echo "${{ github.event.issue.body }}" > ${{ runner.temp }}/_github_workflow/issue.md + + - name: Convert GitHub markdown to Jira markup + uses: docker://pandoc/core:3.4 + with: + args: --from gfm --to jira --output=/github/workflow/issue.jira /github/workflow/issue.md + + - name: Put output to the variable + run: | + echo 'JIRA_CONTENT<> $GITHUB_ENV + cat ${{ runner.temp }}/_github_workflow/issue.jira >> $GITHUB_ENV + echo >> $GITHUB_ENV + echo "----" >> $GITHUB_ENV + echo "GitHub issue: ${{ github.event.issue.html_url }}" >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Login + uses: atlassian/gajira-login@v3 + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + + - name: Create issue + id: create + uses: atlassian/gajira-create@v3 + with: + project: ${{ vars.JIRA_PROJECT_KEY }} + issuetype: Story + summary: "UI Kit (GitHub): ${{ github.event.issue.title }}" + description: ${{ env.JIRA_CONTENT }} + fields: '{"components": [{"name": "UIKit"}]}' diff --git a/.github/workflows/pr-auto-assign-author.yml b/.github/workflows/pr-auto-assign-author.yml index 14d4c01b..65a81e7c 100644 --- a/.github/workflows/pr-auto-assign-author.yml +++ b/.github/workflows/pr-auto-assign-author.yml @@ -13,4 +13,4 @@ jobs: assign-author: runs-on: ubuntu-latest steps: - - uses: toshimaru/auto-author-assign@v2.0.1 + - uses: toshimaru/auto-author-assign@v2.1.1 diff --git a/.github/workflows/release-manual.yml b/.github/workflows/release-manual.yml index a0ce47e5..905f827a 100644 --- a/.github/workflows/release-manual.yml +++ b/.github/workflows/release-manual.yml @@ -34,7 +34,7 @@ jobs: run: npm run build - name: Run tests - run: npm run test + run: npm run test:ci - name: Configure git run: | @@ -65,7 +65,7 @@ jobs: run: git push origin && git push --tags - name: Publish release on GitHub - uses: release-drafter/release-drafter@v5 + uses: release-drafter/release-drafter@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml index c33cf4cc..127d99f2 100644 --- a/.github/workflows/release-notes.yml +++ b/.github/workflows/release-notes.yml @@ -13,6 +13,6 @@ jobs: release-notes: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-storybook.yml b/.github/workflows/release-storybook.yml index 9ddaf825..b0d2725d 100644 --- a/.github/workflows/release-storybook.yml +++ b/.github/workflows/release-storybook.yml @@ -14,7 +14,7 @@ jobs: release-storybook: runs-on: ubuntu-latest steps: - - uses: bitovi/github-actions-storybook-to-github-pages@v1.0.2 + - uses: bitovi/github-actions-storybook-to-github-pages@v1.0.3 with: path: storybook-static install_command: npm install diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fc8aa255..604af0e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,13 +14,65 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [18, 20, 21] + node-version: [18, 20, 21, 22] + steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node }} - - run: npm install && npx playwright install - - run: npm run lint - - run: npm run build - - run: npm run test + node-version: ${{ matrix.node-version }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + + - name: Install NPM dependencies + run: npm install && npx playwright install + + - name: Install PHP dependencies + run: composer install + + - name: Check coding standards + run: npm run lint + + - name: Check Twig coding standards + run: composer lint + + - name: Build + run: npm run build + + - name: Run tests + run: npm run test:ci + + - name: Upload coverage report as an artifact + uses: actions/upload-artifact@v4 + with: + name: ${{github.job}}-code-coverage-report-${{ matrix.node-version }} + path: ./.logs/coverage + if-no-files-found: error + + - name: Upload coverage report to Codecov + uses: codecov/codecov-action@v4 + if: ${{ env.CODECOV_TOKEN != '' }} + with: + files: ./.logs/coverage/clover.xml + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + - name: Deploy to Netlify + if: matrix.node-version == 20 + uses: nwtgck/actions-netlify@v3.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + publish-dir: './storybook-static' + production-branch: main + deploy-message: "Deploy from GitHub Actions" + enable-pull-request-comment: true + enable-commit-comment: true + overwrites-pull-request-comment: true + env: + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + timeout-minutes: 1 diff --git a/.gitignore b/.gitignore index 22bd8e55..b37497e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ +.logs +.twig-cs-fixer.cache +composer.lock dist node_modules package-lock.json storybook-static +vendor diff --git a/.npmignore b/.npmignore index 127bf723..a228e6c0 100644 --- a/.npmignore +++ b/.npmignore @@ -1,5 +1,4 @@ -.storybook -.idea +**/*.test.* .editorconfig .eslintignore .eslintrc.yml @@ -9,10 +8,9 @@ .npmignore .stylelintignore .stylelintrc.json +.twig-cs-fixer.php +composer.json +jest.config.js renovate.json +tests webpack - -# Exclude demo assets as they are erroneously located in dist assets. -# @see https://github.com/civictheme/uikit/issues/30 -assets/images -assets/videos diff --git a/.storybook/preview.js b/.storybook/preview.js index 507f7bdd..770d5a22 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,12 +1,65 @@ +import { decoratorDocs, decoratorStoryLayout } from '../components/00-base/storybook/storybook.utils'; + require('twig'); const storyOrder = [ + 'Welcome', + 'About CivicTheme', 'Base', [ 'Colors', + 'Fonts', + 'Typography', + 'Icon', + 'Background', + 'Elevation', + 'Grid', + 'Layout', + 'Spacing', + 'Item List', + 'Utilities', + 'Storybook', + [ + 'Overview', + '*', + ], + ], + '*', + 'Atoms', + [ + 'Chip', + 'Content Link', + 'Heading', + 'Iframe', + 'Image', + 'Form Controls', + ], + '*', + 'Molecules', + [ + 'Accordion', + 'Attachment', + 'Back To Top', + 'Basic Content', + 'Breadcrumb', + 'Callout', + 'Field', + 'Figure', + 'List', + [ + 'Single Filter', + 'Group Filter', + 'Pagination', + '*', + 'Snippet', + ], '*', ], '*', + 'Organisms', + '*', + 'Templates', + '*', ]; export const parameters = { @@ -26,7 +79,7 @@ export const parameters = { values: [ { name: 'White', - value: '#fff', + value: '#ffffff', }, { name: 'Light', @@ -91,3 +144,28 @@ export const parameters = { }, }, }; + +export const decorators = [decoratorStoryLayout, decoratorDocs]; + +export const globalTypes = { + resizer: { + name: 'Resizer', + description: 'Resize component wrapper', + defaultValue: false, + toolbar: { + icon: 'component', + items: [ + { + value: true, + title: 'Enabled', + type: 'item', + }, + { + value: false, + title: 'Disabled', + type: 'reset', + }, + ], + }, + }, +}; diff --git a/assets/images/demo1.jpg b/.storybook/static/demo/images/demo1.jpg similarity index 100% rename from assets/images/demo1.jpg rename to .storybook/static/demo/images/demo1.jpg diff --git a/assets/images/demo2.jpg b/.storybook/static/demo/images/demo2.jpg similarity index 100% rename from assets/images/demo2.jpg rename to .storybook/static/demo/images/demo2.jpg diff --git a/assets/images/demo3.jpg b/.storybook/static/demo/images/demo3.jpg similarity index 100% rename from assets/images/demo3.jpg rename to .storybook/static/demo/images/demo3.jpg diff --git a/assets/images/demo4.jpg b/.storybook/static/demo/images/demo4.jpg similarity index 100% rename from assets/images/demo4.jpg rename to .storybook/static/demo/images/demo4.jpg diff --git a/assets/images/demo5.jpg b/.storybook/static/demo/images/demo5.jpg similarity index 100% rename from assets/images/demo5.jpg rename to .storybook/static/demo/images/demo5.jpg diff --git a/assets/images/demo6.jpg b/.storybook/static/demo/images/demo6.jpg similarity index 100% rename from assets/images/demo6.jpg rename to .storybook/static/demo/images/demo6.jpg diff --git a/assets/videos/demo.avi b/.storybook/static/demo/videos/demo.avi similarity index 100% rename from assets/videos/demo.avi rename to .storybook/static/demo/videos/demo.avi diff --git a/assets/videos/demo.mp4 b/.storybook/static/demo/videos/demo.mp4 similarity index 100% rename from assets/videos/demo.mp4 rename to .storybook/static/demo/videos/demo.mp4 diff --git a/assets/videos/demo.webm b/.storybook/static/demo/videos/demo.webm similarity index 100% rename from assets/videos/demo.webm rename to .storybook/static/demo/videos/demo.webm diff --git a/assets/videos/demo_poster.png b/.storybook/static/demo/videos/demo_poster.png similarity index 100% rename from assets/videos/demo_poster.png rename to .storybook/static/demo/videos/demo_poster.png diff --git a/.storybook/theme.js b/.storybook/theme.js index 273b2f0b..1914f85f 100644 --- a/.storybook/theme.js +++ b/.storybook/theme.js @@ -5,7 +5,7 @@ import { create } from '@storybook/theming'; export default create({ base: 'light', - brandTitle: 'CivicTheme Library', + brandTitle: 'CivicTheme UI Kit', brandUrl: 'https://github.com/civictheme/uikit', brandImage: './assets/logos/logo_secondary_light_desktop.png', }); diff --git a/.twig-cs-fixer.php b/.twig-cs-fixer.php new file mode 100644 index 00000000..fd36df90 --- /dev/null +++ b/.twig-cs-fixer.php @@ -0,0 +1,15 @@ +addStandard(new TwigCsFixer\Standard\Twig()); + +$finder = new TwigCsFixer\File\Finder(); +$finder->in(__DIR__ . '/components'); + +$config = new TwigCsFixer\Config\Config(); +$config->setRuleset($ruleset); +$config->setFinder($finder); + +return $config; diff --git a/README.md b/README.md index b64577f4..938a9c45 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,20 @@ [![GitHub Pull Requests](https://img.shields.io/github/issues-pr/civictheme/uikit.svg)](https://github.com/civictheme/uikit/pulls) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/civictheme/uikit) [![Test](https://github.com/civictheme/uikit/actions/workflows/test.yml/badge.svg)](https://github.com/civictheme/uikit/actions/workflows/test.yml) +[![codecov](https://codecov.io/gh/civictheme/uikit/graph/badge.svg?token=NMJD1RDUVQ)](https://codecov.io/gh/civictheme/uikit) ![LICENSE](https://img.shields.io/github/license/civictheme/uikit) [![RenovateBot](https://img.shields.io/badge/RenovateBot-enabled-brightgreen.svg?logo=renovatebot)](https://renovatebot.com)

UI component library with Storybook integration

+

https://uikit.civictheme.io/

---- +> [!Tip] +> For support, see [Getting help](https://docs.civictheme.io/getting-help) documentation + ## Features - Atomic design @@ -47,6 +52,17 @@ change. ## Maintenance +### Updating minor dependencies + +```bash +npm install -g npm-check-updates +npx npm-check-updates -u --target minor +``` + +### Pre-release build + +All commits to `main` branch are built as a Storybook and automatically deployed to https://civictheme-uikit.netlify.app/ + ### Build assets npm run build @@ -64,7 +80,7 @@ This will build: npm run lint - npm run lint:fix + npm run lint-fix ### Run Storybook diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..ae85a700 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +Please report found any vulnerabilities to civictheme@salsa.digital. diff --git a/assets/icons/document.svg b/assets/icons/document.svg new file mode 100644 index 00000000..5976f6f4 --- /dev/null +++ b/assets/icons/document.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/down-arrow-large.svg b/assets/icons/down-arrow-large.svg deleted file mode 100644 index a2fca1e2..00000000 --- a/assets/icons/down-arrow-large.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/layer.svg b/assets/icons/layer.svg new file mode 100644 index 00000000..66b47b91 --- /dev/null +++ b/assets/icons/layer.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/layers.svg b/assets/icons/layers.svg new file mode 100644 index 00000000..91500748 --- /dev/null +++ b/assets/icons/layers.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/lock-file.svg b/assets/icons/lock-file.svg new file mode 100644 index 00000000..6082a741 --- /dev/null +++ b/assets/icons/lock-file.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/lock-gallery.svg b/assets/icons/lock-gallery.svg new file mode 100644 index 00000000..4c31419c --- /dev/null +++ b/assets/icons/lock-gallery.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/mobile.svg b/assets/icons/mobile.svg new file mode 100644 index 00000000..d1430093 --- /dev/null +++ b/assets/icons/mobile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/open-source.svg b/assets/icons/open-source.svg new file mode 100644 index 00000000..9a95c043 --- /dev/null +++ b/assets/icons/open-source.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/up-arrow-large.svg b/assets/icons/up-arrow-large.svg deleted file mode 100644 index 765a55ea..00000000 --- a/assets/icons/up-arrow-large.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/users.svg b/assets/icons/users.svg new file mode 100644 index 00000000..0af1bc58 --- /dev/null +++ b/assets/icons/users.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/water-drop.svg b/assets/icons/water-drop.svg new file mode 100644 index 00000000..23399493 --- /dev/null +++ b/assets/icons/water-drop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/web.svg b/assets/icons/web.svg new file mode 100644 index 00000000..b4a14905 --- /dev/null +++ b/assets/icons/web.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/logos/logo_custom_light_desktop_civictheme.svg b/assets/logos/logo_custom_light_desktop_civictheme.svg new file mode 100644 index 00000000..e255347c --- /dev/null +++ b/assets/logos/logo_custom_light_desktop_civictheme.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/logos/logo_custom_light_mobile_civictheme.svg b/assets/logos/logo_custom_light_mobile_civictheme.svg new file mode 100644 index 00000000..e255347c --- /dev/null +++ b/assets/logos/logo_custom_light_mobile_civictheme.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/00-base/_variables.base.scss b/components/00-base/_variables.base.scss index 7a39021b..64704c20 100644 --- a/components/00-base/_variables.base.scss +++ b/components/00-base/_variables.base.scss @@ -186,6 +186,14 @@ $ct-font-weights-default: ( 'black': 900, ); +// Average character width. Used for calculating spacing based on font size. +// @todo: This should be calculated based on the font itself and added to the +// font definition map. +$ct-font-average-character-width: 0.25 !default; + +// Font smoothing. +$ct-font-smoothing-enable: true !default; + // // Typography. // @@ -255,12 +263,12 @@ $ct-typography-default: ( 'm': ($ct-font-base-size, $ct-font-base-line-height * 1.25, 'semibold', 'primary', -0.1px) ), 'label-small': ( - 'xxs': ($ct-font-base-size * 0.875, $ct-font-base-line-height * 1.25, 'regular', 'primary', -0.1px), - 'm': ($ct-font-base-size * 0.875, $ct-font-base-line-height * 1.125, 'regular', 'primary', 0) + 'xxs': ($ct-font-base-size, $ct-font-base-line-height * 1.25, 'regular', 'primary', -0.1px), + 'm': ($ct-font-base-size, $ct-font-base-line-height * 1.25, 'regular', 'primary', -0.1px) ), 'label-extra-small': ( - 'xxs': ($ct-font-base-size * 0.5, $ct-font-base-line-height * 1.25, 'regular', 'primary', -0.1px), - 'm': ($ct-font-base-size * 0.75, $ct-font-base-line-height * 1.125, 'regular', 'primary', 0) + 'xxs': ($ct-font-base-size * 0.875, $ct-font-base-line-height * 1.25, 'regular', 'primary', -0.1px), + 'm': ($ct-font-base-size * 0.875, $ct-font-base-line-height * 1.125, 'regular', 'primary', 0) ), // Special elements. 'quote': ( @@ -269,9 +277,6 @@ $ct-typography-default: ( ), ); -// Font smoothing. -$ct-font-smoothing-enable: true !default; - // // Spacing. // @@ -319,17 +324,27 @@ $ct-grid-space: ct-particle-px() !default; // The lowest breakpoint where column classes should start applying. $ct-grid-lowest-breakpoint: 'xxs' !default; -// The width of the fluid container at max width. Used to contain fluid -// containers on wide screens. Set to 'auto' to keep fluid. -$ct-grid-max-width: map.get($ct-breakpoints, 'xxl') !default; +// The lowest breakpoint where *responsive* grid classes should start applying. +// Must align with the value in the auto-generated column_class in +// the Grid component. +$ct-grid-responsive-breakpoint: 'm' !default; -// Spacing between columns in a row. +// Horizontal spacing between grid items depending on the responsive +// context. $ct-grid-gutters: ( 'xxs': $ct-grid-space * 2, 'xs': $ct-grid-space * 2, 's': $ct-grid-space * 3 ) !default; +// Horizontal spacing between grid items depending on the responsive +// context. +$ct-grid-vertical-gutters: ( + 'xxs': $ct-grid-space * 2, + 'xs': $ct-grid-space * 2, + 's': $ct-grid-space * 3 +) !default; + // Side spacing between the edge of the viewport and a start of the grid. $ct-grid-offsets: ( 'xxs': $ct-grid-space * 3, diff --git a/components/00-base/_variables.components.scss b/components/00-base/_variables.components.scss index e28d4c06..8e31d5d4 100644 --- a/components/00-base/_variables.components.scss +++ b/components/00-base/_variables.components.scss @@ -63,6 +63,20 @@ $ct-collapsible-light-icon-color: $ct-collapsible-light-color !default; $ct-collapsible-dark-color: ct-color-dark('body') !default; $ct-collapsible-dark-icon-color: $ct-collapsible-dark-color !default; +// +// Layout. +// +$ct-layout-columns: $ct-grid-columns; +$ct-layout-sidebar-left-columns: 3; +$ct-layout-sidebar-right-columns: 3; +$ct-layout-column-gap: ct-spacing(3); +$ct-layout-column-gap-left-only: ct-spacing(8); +$ct-layout-column-gap-right-only: ct-spacing(8); +$ct-layout-row-gap: ct-spacing(3); +$ct-layout-row-gap-left-only: ct-spacing(3); +$ct-layout-row-gap-right-only: ct-spacing(3); +$ct-layout-breakpoint: l; + // Outline. $ct-outline-light: ct-color-light('interaction-focus') !default; $ct-outline-dark: ct-color-dark('interaction-focus') !default; @@ -72,12 +86,161 @@ $ct-outline-dark: ct-color-dark('interaction-focus') !default; //////////////////////////////////////////////////////////////////////////////// // -// Button. +// Chip. +// +$ct-chip-border-width: ct-particle(0.125) !default; +$ct-chip-light-background-color: ct-color-light('interaction-text') !default; +$ct-chip-light-border-color: ct-color-light('interaction-background') !default; +$ct-chip-light-color: ct-color-light('interaction-background') !default; +$ct-chip-light-selected-background-color: ct-color-light('interaction-background') !default; +$ct-chip-light-selected-border-color: $ct-chip-light-selected-background-color !default; +$ct-chip-light-selected-color: ct-color-light('interaction-hover-text') !default; +$ct-chip-light-selected-hover-color: $ct-chip-light-background-color !default; +$ct-chip-light-selected-hover-background-color: ct-color-light('interaction-hover-background') !default; +$ct-chip-light-selected-hover-border-color: $ct-chip-light-selected-hover-background-color !default; +$ct-chip-light-hover-background-color: $ct-chip-light-background-color !default; +$ct-chip-light-hover-color: ct-color-light('interaction-hover-background') !default; +$ct-chip-light-hover-border-color: $ct-chip-light-hover-color !default; +$ct-chip-light-focus-background-color: false !default; +$ct-chip-light-focus-border-color: false !default; +$ct-chip-light-focus-color: false !default; +$ct-chip-light-focus-outline-color: ct-color-light('interaction-focus') !default; +$ct-chip-dark-background-color: ct-color-dark('interaction-text') !default; +$ct-chip-dark-border-color: ct-color-dark('interaction-background') !default; +$ct-chip-dark-color: ct-color-dark('interaction-background') !default; +$ct-chip-dark-selected-background-color: ct-color-dark('interaction-background') !default; +$ct-chip-dark-selected-border-color: $ct-chip-dark-selected-background-color !default; +$ct-chip-dark-selected-color: ct-color-dark('interaction-hover-text') !default; +$ct-chip-dark-selected-hover-color: $ct-chip-dark-background-color !default; +$ct-chip-dark-selected-hover-background-color: ct-color-dark('interaction-hover-background') !default; +$ct-chip-dark-selected-hover-border-color: $ct-chip-dark-selected-hover-background-color !default; +$ct-chip-dark-hover-background-color: $ct-chip-dark-background-color !default; +$ct-chip-dark-hover-color: ct-color-dark('interaction-hover-background') !default; +$ct-chip-dark-hover-border-color: $ct-chip-dark-hover-color !default; +$ct-chip-dark-focus-background-color: false !default; +$ct-chip-dark-focus-border-color: false !default; +$ct-chip-dark-focus-color: false !default; +$ct-chip-dark-focus-outline-color: ct-color-dark('interaction-focus') !default; + +// +// Content Link. +// +$ct-content-link-light-color: ct-color-light('interaction-background') !default; +$ct-content-link-light-hover-background-color: ct-color-light('interaction-hover-background') !default; +$ct-content-link-light-hover-color: ct-color-light('interaction-hover-text') !default; +$ct-content-link-light-visited-color: ct-color-light('body') !default; +$ct-content-link-light-visited-hover-color: ct-color-light('interaction-hover-text') !default; +$ct-content-link-light-visited-hover-border-color: ct-color-light('interaction-focus') !default; +$ct-content-link-dark-color: ct-color-dark('interaction-background') !default; +$ct-content-link-dark-hover-background-color: ct-color-dark('interaction-hover-background') !default; +$ct-content-link-dark-hover-color: ct-color-dark('interaction-hover-text') !default; +$ct-content-link-dark-visited-color: ct-color-dark('body') !default; +$ct-content-link-dark-visited-hover-color: ct-color-dark('interaction-hover-text') !default; +$ct-content-link-dark-visited-hover-border-color: ct-color-dark('interaction-focus') !default; + +// +// Heading. +// +$ct-heading-light-color: ct-color-light('heading') !default; +$ct-heading-dark-color: ct-color-dark('heading') !default; + +// +// Iframe. +// +$ct-iframe-space-horizontal: ct-spacing(7) !default; +$ct-iframe-light-wrapper-background-color: ct-color-light('background-light') !default; +$ct-iframe-dark-wrapper-background-color: ct-color-dark('background-light') !default; + +// +// Link. +// +$ct-link-light-color: ct-color-light('interaction-background') !default; +$ct-link-light-hover-color: ct-color-light('interaction-hover-background') !default; +$ct-link-light-visited-color: $ct-link-light-color !default; +$ct-link-light-active-color: ct-color-light('body') !default; +$ct-link-dark-color: ct-color-dark('interaction-background') !default; +$ct-link-dark-hover-color: ct-color-dark('interaction-hover-background') !default; +$ct-link-dark-visited-color: $ct-link-dark-color !default; +$ct-link-dark-active-color: ct-color-dark('body') !default; + +// +// Popover. +// +$ct-popover-min-width: ct-particle(35) !default; +$ct-popover-description-border-radius: $ct-border-radius !default; +$ct-popover-z-index: 99 !default; +$ct-popover-top-offset: ct-particle(2) !default; +$ct-popover-light-content-background-color: ct-color-light('background-light') !default; +$ct-popover-dark-content-background-color: ct-color-dark('background') !default; + +// +// Table. +// +$ct-table-breakpoint: 'm' !default; +$ct-table-cell-vertical-align: top !default; +$ct-table-light-background-color: ct-color-light('background-light') !default; +$ct-table-light-border-color: ct-color-light('border') !default; +$ct-table-light-caption-color: ct-color-light('body') !default; +$ct-table-light-color: ct-color-light('body') !default; +$ct-table-light-footer-border-color: ct-color-light('border') !default; +$ct-table-light-footer-color: ct-color-light('heading') !default; +$ct-table-light-header-border-color: ct-color-light('border') !default; +$ct-table-light-header-color: ct-color-light('heading') !default; +$ct-table-light-row-even-background-color: ct-color-light('background') !default; +$ct-table-light-row-even-color: ct-color-light('body') !default; +$ct-table-light-row-odd-background-color: ct-color-light('background-light') !default; +$ct-table-light-row-odd-color: ct-color-light('body') !default; +$ct-table-dark-background-color: ct-color-dark('background') !default; +$ct-table-dark-border-color: ct-color-dark('border') !default; +$ct-table-dark-caption-color: ct-color-dark('body') !default; +$ct-table-dark-color: ct-color-dark('body') !default; +$ct-table-dark-footer-border-color: ct-color-dark('border') !default; +$ct-table-dark-footer-color: ct-color-dark('heading') !default; +$ct-table-dark-header-border-color: ct-color-dark('border') !default; +$ct-table-dark-header-color: ct-color-dark('heading') !default; +$ct-table-dark-row-even-background-color: ct-color-dark('background-light') !default; +$ct-table-dark-row-even-color: ct-color-dark('body') !default; +$ct-table-dark-row-odd-background-color: ct-color-dark('background') !default; +$ct-table-dark-row-odd-color: ct-color-dark('body') !default; + +// +// Tag. +// +$ct-tag-border-radius: $ct-border-radius !default; +$ct-tag-border-width: ct-particle(0.125) !default; +$ct-tag-light-primary-background-color: ct-color-light('interaction-background') !default; +$ct-tag-light-primary-border-color: $ct-tag-light-primary-background-color !default; +$ct-tag-light-primary-color: ct-color-light('interaction-text') !default; +$ct-tag-light-secondary-background-color: transparent !default; +$ct-tag-light-secondary-border-color: ct-color-light('interaction-background') !default; +$ct-tag-light-secondary-color: ct-color-light('interaction-background') !default; +$ct-tag-light-tertiary-background-color: transparent !default; +$ct-tag-light-tertiary-border-color: transparent !default; +$ct-tag-light-tertiary-color: ct-color-light('interaction-background') !default; +$ct-tag-dark-primary-background-color: ct-color-dark('interaction-background') !default; +$ct-tag-dark-primary-border-color: $ct-tag-dark-primary-background-color !default; +$ct-tag-dark-primary-color: ct-color-dark('interaction-text') !default; +$ct-tag-dark-secondary-background-color: transparent !default; +$ct-tag-dark-secondary-border-color: ct-color-dark('interaction-background') !default; +$ct-tag-dark-secondary-color: ct-color-dark('interaction-background') !default; +$ct-tag-dark-tertiary-background-color: transparent !default; +$ct-tag-dark-tertiary-border-color: transparent !default; +$ct-tag-dark-tertiary-color: ct-color-dark('interaction-background') !default; + +// +// Video +// +$ct-video-ratio-width: 16 !default; +$ct-video-ratio-height: 9 !default; + +// +// Form Control/Button. // $ct-button-border-radius: ct-particle(0.5) !default; $ct-button-border-width: ct-particle(0.25) !default; $ct-button-outline-offset: ct-particle(0.25) !default; $ct-button-outline-width: ct-particle(0.375) !default; +$ct-button-animation-duration: $ct-animation-duration; // Primary button. $ct-button-light-primary-background-color: ct-color-light('interaction-background') !default; @@ -182,96 +345,55 @@ $ct-button-dark-tertiary-focus-color: false !default; $ct-button-dark-tertiary-focus-outline-color: ct-color-dark('interaction-focus') !default; // -// Chip. +// Form Control/Checkbox. // -$ct-chip-border-width: ct-particle(0.125) !default; -$ct-chip-light-background-color: ct-color-light('interaction-text') !default; -$ct-chip-light-border-color: ct-color-light('interaction-background') !default; -$ct-chip-light-color: ct-color-light('interaction-background') !default; -$ct-chip-light-selected-background-color: ct-color-light('interaction-hover-background') !default; -$ct-chip-light-selected-border-color: ct-color-light('interaction-hover-background') !default; -$ct-chip-light-selected-color: ct-color-light('interaction-hover-text') !default; -$ct-chip-light-hover-background-color: $ct-chip-light-selected-background-color !default; -$ct-chip-light-hover-border-color: $ct-chip-light-selected-border-color !default; -$ct-chip-light-hover-color: $ct-chip-light-selected-color !default; -$ct-chip-light-focus-background-color: false !default; -$ct-chip-light-focus-border-color: false !default; -$ct-chip-light-focus-color: false !default; -$ct-chip-light-focus-outline-color: ct-color-light('interaction-focus') !default; -$ct-chip-dark-background-color: ct-color-dark('interaction-text') !default; -$ct-chip-dark-border-color: ct-color-dark('interaction-background') !default; -$ct-chip-dark-color: ct-color-dark('interaction-background') !default; -$ct-chip-dark-selected-background-color: ct-color-dark('interaction-hover-background') !default; -$ct-chip-dark-selected-border-color: ct-color-dark('interaction-hover-background') !default; -$ct-chip-dark-selected-color: ct-color-dark('interaction-hover-text') !default; -$ct-chip-dark-hover-background-color: $ct-chip-dark-selected-background-color !default; -$ct-chip-dark-hover-border-color: $ct-chip-dark-selected-border-color !default; -$ct-chip-dark-hover-color: $ct-chip-dark-selected-color !default; -$ct-chip-dark-focus-background-color: false !default; -$ct-chip-dark-focus-border-color: false !default; -$ct-chip-dark-focus-color: false !default; -$ct-chip-dark-focus-outline-color: ct-color-dark('interaction-focus') !default; - -// -// Input. -// -$ct-input-border-radius: $ct-border-radius, 1 !default; -$ct-input-light-background-color: ct-color-light('background-light') !default; -$ct-input-light-border-color: ct-color-light('border-light') !default; -$ct-input-light-color: ct-color-light('body') !default; -$ct-input-light-hover-border-color: ct-color-light('interaction-background') !default; -$ct-input-light-focus-background-color: ct-color-light('background-light') !default; -$ct-input-light-focus-color: ct-color-light('body') !default; -$ct-input-light-focus-outline-color: ct-color-light('interaction-focus') !default; -$ct-input-light-focus-border-color: ct-color-light('border') !default; -$ct-input-light-error-border-color: ct-color-light('error') !default; -$ct-input-light-success-border-color: ct-color-light('success') !default; -$ct-input-dark-background-color: ct-color-dark('background-light') !default; -$ct-input-dark-border-color: ct-color-dark('border-light') !default; -$ct-input-dark-color: ct-color-dark('body') !default; -$ct-input-dark-hover-border-color: ct-color-dark('interaction-background') !default; -$ct-input-dark-focus-border-color: ct-color-dark('border') !default; -$ct-input-dark-error-border-color: ct-color-dark('error') !default; -$ct-input-dark-success-border-color: ct-color-dark('success') !default; - -// -// Radio. -// -$ct-radio-label-light-color: ct-color-light('body') !default; -$ct-radio-light-border-color: ct-color-light('border-light') !default; -$ct-radio-light-selected-background-color: ct-color-light('interaction-background') !default; -$ct-radio-light-active-background-color: ct-color-light('body') !default; -$ct-radio-light-check-color: ct-color-light('interaction-text') !default; -$ct-radio-label-dark-color: ct-color-dark('body') !default; -$ct-radio-dark-border-color: ct-color-dark('border-dark') !default; -$ct-radio-dark-selected-background-color: ct-color-dark('interaction-background') !default; -$ct-radio-dark-active-background-color: ct-color-dark('body') !default; -$ct-radio-dark-check-color: ct-color-dark('interaction-text') !default; - -// -// Checkbox. -// -$ct-checkbox-border-radius: math.div($ct-border-radius, 2) !default; -$ct-checkbox-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='' title='approvetick' width='22' height='22' viewBox='0 0 24 24' aria-hidden='true'%3E%3Cpath d='M18.7104 7.20998C18.6175 7.11625 18.5069 7.04186 18.385 6.99109C18.2632 6.94032 18.1324 6.91418 18.0004 6.91418C17.8684 6.91418 17.7377 6.94032 17.6159 6.99109C17.494 7.04186 17.3834 7.11625 17.2904 7.20998L9.84044 14.67L6.71044 11.53C6.61392 11.4367 6.49998 11.3634 6.37512 11.3142C6.25026 11.265 6.11694 11.2409 5.98276 11.2432C5.84858 11.2455 5.71617 11.2743 5.59309 11.3278C5.47001 11.3812 5.35868 11.4585 5.26544 11.555C5.1722 11.6515 5.09889 11.7654 5.04968 11.8903C5.00048 12.0152 4.97635 12.1485 4.97867 12.2827C4.98099 12.4168 5.00972 12.5493 5.06321 12.6723C5.1167 12.7954 5.19392 12.9067 5.29044 13L9.13044 16.84C9.2234 16.9337 9.334 17.0081 9.45586 17.0589C9.57772 17.1096 9.70843 17.1358 9.84044 17.1358C9.97245 17.1358 10.1032 17.1096 10.225 17.0589C10.3469 17.0081 10.4575 16.9337 10.5504 16.84L18.7104 8.67998C18.8119 8.58634 18.893 8.47269 18.9484 8.34619C19.0038 8.21969 19.0324 8.08308 19.0324 7.94498C19.0324 7.80688 19.0038 7.67028 18.9484 7.54378C18.893 7.41728 18.8119 7.30363 18.7104 7.20998Z'%3E%3C/path%3E%3C/svg%3E") !default; -$ct-checkbox-label-light-color: ct-color-light('body') !default; +$ct-checkbox-border-radius: $ct-border-radius !default; +$ct-checkbox-check: url("data:image/svg+xml,%3Csvg viewBox='' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0.9 0H17.1C17.3387 0 17.5676 0.0948211 17.7364 0.263604C17.9052 0.432387 18 0.661305 18 0.9V17.1C18 17.3387 17.9052 17.5676 17.7364 17.7364C17.5676 17.9052 17.3387 18 17.1 18H0.9C0.661305 18 0.432387 17.9052 0.263604 17.7364C0.0948211 17.5676 0 17.3387 0 17.1V0.9C0 0.661305 0.0948211 0.432387 0.263604 0.263604C0.432387 0.0948211 0.661305 0 0.9 0ZM6.48885 14.1746C6.57252 14.259 6.67206 14.326 6.78173 14.3716C6.8914 14.4173 7.00904 14.4409 7.12785 14.4409C7.24666 14.4409 7.3643 14.4173 7.47397 14.3716C7.58364 14.326 7.68318 14.259 7.76685 14.1746L15.9388 6.00264C16.1083 5.83317 16.2035 5.60331 16.2035 5.36364C16.2035 5.12397 16.1083 4.89412 15.9388 4.72464C15.7694 4.55517 15.5395 4.45996 15.2998 4.45996C15.0602 4.45996 14.8303 4.55517 14.6608 4.72464L7.12785 12.2666L4.23885 9.36864C4.06938 9.19917 3.83952 9.10396 3.59985 9.10396C3.36018 9.10396 3.13032 9.19917 2.96085 9.36864C2.79138 9.53812 2.69617 9.76797 2.69617 10.0076C2.69617 10.2473 2.79138 10.4772 2.96085 10.6466L6.48885 14.1746Z' fill='currentcolor'/%3E%3C/svg%3E%0A"); +$ct-checkbox-animation-duration: 0.1s !default; +$ct-checkbox-disabled-opacity: 40% !default; +$ct-checkbox-outline-width: 2px !default; +$ct-checkbox-outline-offset: 2px !default; +$ct-checkbox-light-background-color: transparent !default; $ct-checkbox-light-border-color: ct-color-light('border-light') !default; -$ct-checkbox-light-selected-background-color: ct-color-light('interaction-background') !default; -$ct-checkbox-light-active-background-color: ct-color-light('body') !default; -$ct-checkbox-light-check-color: ct-color-light('interaction-text') !default; -$ct-checkbox-label-dark-color: ct-color-dark('body') !default; -$ct-checkbox-dark-border-color: ct-color-dark('border-dark') !default; -$ct-checkbox-dark-selected-background-color: ct-color-dark('interaction-background') !default; -$ct-checkbox-dark-active-background-color: ct-color-dark('body') !default; -$ct-checkbox-dark-check-color: ct-color-dark('interaction-text') !default; - -// -// Field description. +$ct-checkbox-light-color: ct-color-light('body') !default; +$ct-checkbox-light-hover-background-color: $ct-checkbox-light-background-color !default; +$ct-checkbox-light-hover-border-color: ct-color-light('interaction-hover-background') !default; +$ct-checkbox-light-hover-color: $ct-checkbox-light-color !default; +$ct-checkbox-light-checked-background-color: $ct-checkbox-light-background-color !default; +$ct-checkbox-light-checked-border-color: ct-color-light('interaction-background') !default; +$ct-checkbox-light-checked-color: $ct-checkbox-light-color !default; +$ct-checkbox-light-checked-hover-background-color: $ct-checkbox-light-hover-background-color !default; +$ct-checkbox-light-checked-hover-border-color: $ct-checkbox-light-hover-border-color !default; +$ct-checkbox-light-checked-hover-color: $ct-checkbox-light-hover-color !default; +$ct-checkbox-light-invalid-background-color: $ct-checkbox-light-hover-background-color !default; +$ct-checkbox-light-invalid-border-color: ct-color-light('error') !default; +$ct-checkbox-light-invalid-color: ct-color-light('error') !default; +$ct-checkbox-light-outline-color: $ct-outline-light !default; +$ct-checkbox-dark-background-color: transparent !default; +$ct-checkbox-dark-border-color: ct-color-dark('border-light') !default; +$ct-checkbox-dark-color: ct-color-dark('body') !default; +$ct-checkbox-dark-hover-background-color: $ct-checkbox-dark-background-color !default; +$ct-checkbox-dark-hover-border-color: ct-color-dark('interaction-hover-background') !default; +$ct-checkbox-dark-hover-color: $ct-checkbox-dark-color !default; +$ct-checkbox-dark-checked-background-color: $ct-checkbox-dark-background-color !default; +$ct-checkbox-dark-checked-border-color: ct-color-dark('interaction-background') !default; +$ct-checkbox-dark-checked-color: $ct-checkbox-dark-color !default; +$ct-checkbox-dark-checked-hover-background-color: $ct-checkbox-dark-hover-background-color !default; +$ct-checkbox-dark-checked-hover-border-color: $ct-checkbox-dark-hover-border-color !default; +$ct-checkbox-dark-checked-hover-color: $ct-checkbox-dark-hover-color !default; +$ct-checkbox-dark-invalid-background-color: $ct-checkbox-dark-hover-background-color !default; +$ct-checkbox-dark-invalid-border-color: ct-color-dark('error') !default; +$ct-checkbox-dark-invalid-color: ct-color-dark('error') !default; +$ct-checkbox-dark-outline-color: $ct-outline-dark !default; + +// +// Form Control/Field description. // $ct-field-description-light-color: ct-color-light('body') !default; $ct-field-description-dark-color: ct-color-dark('body') !default; // -// Field message. +// Form Control/Field message. // $ct-field-message-border-radius: $ct-border-radius !default; $ct-field-message-light-error-background-color: ct-color-tint(ct-color-constant-light('error'), 85) !default; @@ -290,161 +412,255 @@ $ct-field-message-light-warning-background-color: ct-color-tint(ct-color-constan $ct-field-message-light-warning-border-color: ct-color-light('warning') !default; $ct-field-message-light-warning-color: ct-color-light('body') !default; $ct-field-message-light-warning-icon-color: $ct-field-message-light-warning-color !default; -$ct-field-message-dark-error-background-color: ct-color-shade(ct-color-constant-dark('error'), 30) !default; +$ct-field-message-dark-error-background-color: ct-color-shade(ct-color-constant-dark('error'), 20) !default; $ct-field-message-dark-error-border-color: ct-color-dark('error') !default; $ct-field-message-dark-error-color: ct-color-dark('body') !default; $ct-field-message-dark-error-icon-color: $ct-field-message-dark-error-color !default; -$ct-field-message-dark-information-background-color: ct-color-shade(ct-color-constant-dark('information'), 30) !default; +$ct-field-message-dark-information-background-color: ct-color-shade(ct-color-constant-dark('information'), 20) !default; $ct-field-message-dark-information-border-color: ct-color-dark('information') !default; $ct-field-message-dark-information-color: ct-color-dark('body') !default; $ct-field-message-dark-information-icon-color: $ct-field-message-dark-information-color !default; $ct-field-message-dark-success-border-color: ct-color-dark('success') !default; -$ct-field-message-dark-success-background-color: ct-color-shade(ct-color-constant-dark('success'), 30) !default; +$ct-field-message-dark-success-background-color: ct-color-shade(ct-color-constant-dark('success'), 20) !default; $ct-field-message-dark-success-color: ct-color-dark('body') !default; $ct-field-message-dark-success-icon-color: $ct-field-message-dark-success-color !default; -$ct-field-message-dark-warning-background-color: ct-color-shade(ct-color-constant-dark('warning'), 30) !default; +$ct-field-message-dark-warning-background-color: ct-color-shade(ct-color-constant-dark('warning'), 20) !default; $ct-field-message-dark-warning-border-color: ct-color-dark('warning') !default; $ct-field-message-dark-warning-color: ct-color-dark('body') !default; $ct-field-message-dark-warning-icon-color: $ct-field-message-dark-warning-color !default; // -// Field label. +// Form/Fieldset. +// +$ct-fieldset-stripe-width: $ct-stripe-size !default; +$ct-fieldset-light-required-color: ct-color-light('error') !default; +$ct-fieldset-light-stripe-border-color: ct-color-light('highlight') !default; +$ct-fieldset-dark-required-color: ct-color-dark('error') !default; +$ct-fieldset-light-stripe-border-color: ct-color-dark('highlight') !default; + +// +// Form Control/Input. // +$ct-input-border-radius: $ct-border-radius !default; +$ct-input-outline-width: 2px !default; +$ct-input-outline-offset: 2px !default; +$ct-input-disabled-opacity: 40% !default; +$ct-input-light-background-color: ct-color-light('background-light') !default; +$ct-input-light-border-color: ct-color-light('border') !default; +$ct-input-light-color: ct-color-light('body') !default; +$ct-input-light-hover-background-color: $ct-input-light-background-color !default; +$ct-input-light-hover-border-color: ct-color-light('interaction-hover-background') !default; +$ct-input-light-hover-color: $ct-input-light-color !default; +$ct-input-light-focus-background-color: $ct-input-light-background-color !default; +$ct-input-light-focus-border-color: ct-color-light('interaction-background') !default; +$ct-input-light-focus-color: $ct-input-light-color !default; +$ct-input-light-invalid-background-color: ct-color-light('background-light') !default; +$ct-input-light-invalid-border-color: ct-color-light('error') !default; +$ct-input-light-invalid-color: ct-color-light('body') !default; +$ct-input-light-outline-color: $ct-outline-light !default; +$ct-input-dark-background-color: ct-color-dark('background-dark') !default; +$ct-input-dark-border-color: ct-color-dark('border-light') !default; +$ct-input-dark-color: ct-color-dark('body') !default; +$ct-input-dark-hover-background-color: $ct-input-dark-background-color !default; +$ct-input-dark-hover-border-color: ct-color-dark('interaction-hover-background') !default; +$ct-input-dark-hover-color: $ct-input-dark-color !default; +$ct-input-dark-focus-background-color: ct-color-dark('background') !default; +$ct-input-dark-focus-border-color: ct-color-dark('interaction-background') !default; +$ct-input-dark-focus-color: $ct-input-dark-color !default; +$ct-input-dark-invalid-background-color: ct-color-dark('background-dark') !default; +$ct-input-dark-invalid-border-color: ct-color-dark('error') !default; +$ct-input-dark-invalid-color: ct-color-dark('body') !default; +$ct-input-dark-outline-color: $ct-outline-dark !default; + +// +// Form Control/Label. +// +$ct-label-required-content: '*'; $ct-label-light-color: ct-color-light('heading') !default; $ct-label-light-required-color: ct-color-light('error') !default; $ct-label-dark-color: ct-color-dark('heading') !default; $ct-label-dark-required-color: ct-color-dark('error') !default; // -// Select. +// Form Control/Radio. // +$ct-radio-disabled-opacity: 40% !default; +$ct-radio-animation-duration: 0.1s !default; +$ct-radio-outline-width: 2px !default; +$ct-radio-outline-offset: 2px !default; +$ct-radio-light-background-color: transparent !default; +$ct-radio-light-border-color: ct-color-light('border-light') !default; +$ct-radio-light-color: ct-color-light('body') !default; +$ct-radio-light-hover-background-color: $ct-radio-light-background-color !default; +$ct-radio-light-hover-border-color: ct-color-light('interaction-hover-background') !default; +$ct-radio-light-hover-color: $ct-radio-light-color !default; +$ct-radio-light-checked-background-color: $ct-radio-light-background-color !default; +$ct-radio-light-checked-border-color: ct-color-light('interaction-background') !default; +$ct-radio-light-checked-color: $ct-radio-light-color !default; +$ct-radio-light-checked-hover-background-color: $ct-radio-light-hover-background-color !default; +$ct-radio-light-checked-hover-border-color: $ct-radio-light-hover-border-color !default; +$ct-radio-light-checked-hover-color: $ct-radio-light-hover-color !default; +$ct-radio-light-invalid-background-color: $ct-radio-light-hover-background-color !default; +$ct-radio-light-invalid-border-color: ct-color-light('error') !default; +$ct-radio-light-invalid-color: ct-color-light('error') !default; +$ct-radio-light-outline-color: $ct-outline-light !default; +$ct-radio-dark-background-color: transparent !default; +$ct-radio-dark-border-color: ct-color-dark('border-light') !default; +$ct-radio-dark-color: ct-color-dark('body') !default; +$ct-radio-dark-hover-background-color: $ct-radio-dark-background-color !default; +$ct-radio-dark-hover-border-color: ct-color-dark('interaction-hover-background') !default; +$ct-radio-dark-hover-color: $ct-radio-dark-color !default; +$ct-radio-dark-checked-background-color: $ct-radio-dark-background-color !default; +$ct-radio-dark-checked-border-color: ct-color-dark('interaction-background') !default; +$ct-radio-dark-checked-color: $ct-radio-dark-color !default; +$ct-radio-dark-checked-hover-background-color: $ct-radio-dark-hover-background-color !default; +$ct-radio-dark-checked-hover-border-color: $ct-radio-dark-hover-border-color !default; +$ct-radio-dark-checked-hover-color: $ct-radio-dark-hover-color !default; +$ct-radio-dark-invalid-background-color: $ct-radio-dark-hover-background-color !default; +$ct-radio-dark-invalid-border-color: ct-color-dark('error') !default; +$ct-radio-dark-invalid-color: ct-color-dark('error') !default; +$ct-radio-dark-outline-color: $ct-outline-dark !default; + +// +// Form Control/Select. +// +$ct-select-border-radius: $ct-border-radius !default; +$ct-select-outline-width: 2px !default; +$ct-select-outline-offset: 2px !default; +$ct-select-disabled-opacity: 40% !default; $ct-select-light-background-color: ct-color-light('background-light') !default; +$ct-select-light-border-color: ct-color-light('border') !default; $ct-select-light-color: ct-color-light('body') !default; -$ct-select-light-hover-border-color: ct-color-light('border-light') !default; +$ct-select-light-option-background-color: ct-color-light('background') !default; +$ct-select-light-option-color: inherit !default; +$ct-select-light-hover-background-color: $ct-select-light-background-color !default; +$ct-select-light-hover-border-color: ct-color-light('interaction-hover-background') !default; +$ct-select-light-hover-color: $ct-select-light-color !default; +$ct-select-light-focus-background-color: $ct-select-light-background-color !default; $ct-select-light-focus-border-color: ct-color-light('interaction-background') !default; -$ct-select-light-error-border-color: ct-color-light('error') !default; -$ct-select-light-option-background-color: $ct-select-light-background-color !default; -$ct-select-dark-background-color: ct-color-dark('background-light') !default; +$ct-select-light-focus-color: $ct-select-light-color !default; +$ct-select-light-invalid-background-color: ct-color-light('background-light') !default; +$ct-select-light-invalid-border-color: ct-color-light('error') !default; +$ct-select-light-invalid-color: ct-color-light('body') !default; +$ct-select-light-outline-color: $ct-outline-light !default; +$ct-select-light-icon: url("data:image/svg+xml,%3Csvg width='38' height='28' viewBox='0 0 38 28' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 0H0V28H1V0Z' fill='black' fill-opacity='0.6'/%3E%3Cpath d='M17.0916 13.1714L19 13.1666C19 13.1666 20.7334 13.1666 20.9083 13.1666C21.0831 13.1666 21.0831 13.1666 21.0831 13.1666L21.5 13.1714C21.5 13.1714 21.7189 13.1497 21.8204 13.1074C21.922 13.0651 22.0141 13.0031 22.0916 12.9249C22.1697 12.8475 22.2317 12.7553 22.274 12.6538C22.3163 12.5522 22.3381 12.4433 22.3381 12.3333C22.3381 12.2233 22.3163 12.1144 22.274 12.0128C22.2317 11.9113 22.1697 11.8191 22.0916 11.7416L19.5916 9.24162C19.5141 9.16351 19.422 9.10151 19.3204 9.05921C19.2189 9.0169 19.11 8.99512 19 8.99512C18.8899 8.99512 18.781 9.0169 18.6795 9.05921C18.5779 9.10151 18.4858 9.16351 18.4083 9.24162L15.9083 11.7416C15.7514 11.8985 15.6632 12.1114 15.6632 12.3333C15.6632 12.5552 15.7514 12.768 15.9083 12.9249C16.0652 13.0819 16.278 13.17 16.5 13.17C16.7219 13.17 17.0916 13.1714 17.0916 13.1714ZM20.9083 15.6666H19H17.0916C16.9165 15.6666 16.9165 15.6632 16.8202 15.6632C16.4998 15.6632 16.6098 15.6632 16.5 15.6632C16.3901 15.6632 16.2813 15.6848 16.1797 15.7269C16.0782 15.7689 15.986 15.8306 15.9083 15.9083C15.8306 15.986 15.769 16.0782 15.7269 16.1797C15.6849 16.2813 15.6632 16.3901 15.6632 16.4999C15.6632 16.6098 15.6849 16.7186 15.7269 16.8202C15.769 16.9217 15.8306 17.0139 15.9083 17.0916L18.4083 19.5916C18.4858 19.6697 18.5779 19.7317 18.6795 19.774C18.781 19.8163 18.8899 19.8381 19 19.8381C19.11 19.8381 19.2189 19.8163 19.3204 19.774C19.422 19.7317 19.5141 19.6697 19.5916 19.5916L22.0916 17.0916C22.2485 16.9347 22.3367 16.7219 22.3367 16.4999C22.3367 16.278 22.2485 16.0652 22.0916 15.9083C21.9347 15.7514 21.7219 15.6632 21.5 15.6632C21.278 15.6632 21.0831 15.6666 20.9083 15.6666Z' fill='black' fill-opacity='0.6'/%3E%3C/svg%3E%0A"); +$ct-select-dark-background-color: ct-color-dark('background-dark') !default; +$ct-select-dark-border-color: ct-color-dark('border-light') !default; $ct-select-dark-color: ct-color-dark('body') !default; -$ct-select-dark-hover-border-color: ct-color-dark('border-light') !default; +$ct-select-dark-option-background-color: ct-color-dark('background-light') !default; +$ct-select-dark-option-color: inherit !default; +$ct-select-dark-hover-background-color: $ct-select-dark-background-color !default; +$ct-select-dark-hover-border-color: ct-color-dark('interaction-hover-background') !default; +$ct-select-dark-hover-color: $ct-select-dark-color !default; +$ct-select-dark-focus-background-color: ct-color-dark('background') !default; $ct-select-dark-focus-border-color: ct-color-dark('interaction-background') !default; -$ct-select-dark-error-border-color: ct-color-dark('error') !default; -$ct-select-dark-option-background-color: $ct-select-dark-background-color !default; - -// -// Content Link. -// -$ct-content-link-light-color: ct-color-light('interaction-background') !default; -$ct-content-link-light-hover-background-color: ct-color-light('interaction-hover-background') !default; -$ct-content-link-light-hover-color: ct-color-light('interaction-hover-text') !default; -$ct-content-link-light-visited-color: ct-color-light('body') !default; -$ct-content-link-light-visited-hover-color: ct-color-light('interaction-hover-text') !default; -$ct-content-link-light-visited-hover-border-color: ct-color-light('interaction-focus') !default; -$ct-content-link-dark-color: ct-color-dark('interaction-background') !default; -$ct-content-link-dark-hover-background-color: ct-color-dark('interaction-hover-background') !default; -$ct-content-link-dark-hover-color: ct-color-dark('interaction-hover-text') !default; -$ct-content-link-dark-visited-color: ct-color-dark('body') !default; -$ct-content-link-dark-visited-hover-color: ct-color-dark('interaction-hover-text') !default; -$ct-content-link-dark-visited-hover-border-color: ct-color-dark('interaction-focus') !default; - -// -// Link. -// -$ct-link-light-color: ct-color-light('interaction-background') !default; -$ct-link-light-hover-color: ct-color-light('interaction-hover-background') !default; -$ct-link-light-visited-color: $ct-link-light-color !default; -$ct-link-light-active-color: ct-color-light('body') !default; -$ct-link-dark-color: ct-color-dark('interaction-background') !default; -$ct-link-dark-hover-color: ct-color-dark('interaction-hover-background') !default; -$ct-link-dark-visited-color: $ct-link-dark-color !default; -$ct-link-dark-active-color: ct-color-dark('body') !default; - -// -// Heading. -// -$ct-heading-light-color: ct-color-light('heading') !default; -$ct-heading-dark-color: ct-color-dark('heading') !default; - -// -// Tag. -// -$ct-tag-border-radius: $ct-border-radius !default; -$ct-tag-border-width: ct-particle(0.125) !default; -$ct-tag-light-primary-background-color: ct-color-light('interaction-background') !default; -$ct-tag-light-primary-border-color: $ct-tag-light-primary-background-color !default; -$ct-tag-light-primary-color: ct-color-light('interaction-text') !default; -$ct-tag-light-secondary-background-color: transparent !default; -$ct-tag-light-secondary-border-color: ct-color-light('interaction-background') !default; -$ct-tag-light-secondary-color: ct-color-light('interaction-background') !default; -$ct-tag-light-tertiary-background-color: transparent !default; -$ct-tag-light-tertiary-border-color: transparent !default; -$ct-tag-light-tertiary-color: ct-color-light('interaction-background') !default; -$ct-tag-dark-primary-background-color: ct-color-dark('interaction-background') !default; -$ct-tag-dark-primary-border-color: $ct-tag-dark-primary-background-color !default; -$ct-tag-dark-primary-color: ct-color-dark('interaction-text') !default; -$ct-tag-dark-secondary-background-color: transparent !default; -$ct-tag-dark-secondary-border-color: ct-color-dark('interaction-background') !default; -$ct-tag-dark-secondary-color: ct-color-dark('interaction-background') !default; -$ct-tag-dark-tertiary-background-color: transparent !default; -$ct-tag-dark-tertiary-border-color: transparent !default; -$ct-tag-dark-tertiary-color: ct-color-dark('interaction-background') !default; - -// -// Table. -// -$ct-table-breakpoint: 'm' !default; -$ct-table-light-background-color: ct-color-light('background-light') !default; -$ct-table-light-border-color: ct-color-light('border') !default; -$ct-table-light-caption-color: ct-color-light('body') !default; -$ct-table-light-color: ct-color-light('body') !default; -$ct-table-light-footer-border-color: ct-color-light('border') !default; -$ct-table-light-footer-color: ct-color-light('heading') !default; -$ct-table-light-header-border-color: ct-color-light('border') !default; -$ct-table-light-header-color: ct-color-light('heading') !default; -$ct-table-light-row-even-background-color: ct-color-light('background') !default; -$ct-table-light-row-even-color: ct-color-light('body') !default; -$ct-table-light-row-odd-background-color: ct-color-light('background-light') !default; -$ct-table-light-row-odd-color: ct-color-light('body') !default; -$ct-table-dark-background-color: ct-color-dark('background') !default; -$ct-table-dark-border-color: ct-color-dark('border') !default; -$ct-table-dark-caption-color: ct-color-dark('body') !default; -$ct-table-dark-color: ct-color-dark('body') !default; -$ct-table-dark-footer-border-color: ct-color-dark('border') !default; -$ct-table-dark-footer-color: ct-color-dark('heading') !default; -$ct-table-dark-header-border-color: ct-color-dark('border') !default; -$ct-table-dark-header-color: ct-color-dark('heading') !default; -$ct-table-dark-row-even-background-color: ct-color-dark('background-light') !default; -$ct-table-dark-row-even-color: ct-color-dark('body') !default; -$ct-table-dark-row-odd-background-color: ct-color-dark('background') !default; -$ct-table-dark-row-odd-color: ct-color-dark('body') !default; - -// -// Iframe. -// -$ct-iframe-space-horizontal: ct-spacing(7) !default; -$ct-iframe-light-wrapper-background-color: ct-color-light('background-light') !default; -$ct-iframe-dark-wrapper-background-color: ct-color-dark('background-light') !default; - -// -// Popover. -// -$ct-popover-min-width: ct-particle(35) !default; -$ct-popover-description-border-radius: $ct-border-radius !default; -$ct-popover-z-index: 99 !default; -$ct-popover-top-offset: ct-particle(2) !default; -$ct-popover-light-content-background-color: ct-color-light('background-light') !default; -$ct-popover-dark-content-background-color: ct-color-dark('background') !default; - -// -// Video -// -$ct-video-ratio-width: 16 !default; -$ct-video-ratio-height: 9 !default; +$ct-select-dark-focus-color: $ct-select-dark-color !default; +$ct-select-dark-invalid-background-color: ct-color-dark('background') !default; +$ct-select-dark-invalid-border-color: ct-color-dark('error') !default; +$ct-select-dark-invalid-color: ct-color-dark('body') !default; +$ct-select-dark-outline-color: $ct-outline-dark !default; +$ct-select-dark-icon: url("data:image/svg+xml,%3Csvg width='38' height='28' viewBox='0 0 38 28' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 0H0V28H1V0Z' fill='white' fill-opacity='0.9'/%3E%3Cpath d='M17.0916 13.1714L19 13.1666C19 13.1666 20.7334 13.1666 20.9083 13.1666C21.0831 13.1666 21.0831 13.1666 21.0831 13.1666L21.5 13.1714C21.5 13.1714 21.7189 13.1497 21.8204 13.1074C21.922 13.0651 22.0141 13.0031 22.0916 12.9249C22.1697 12.8475 22.2317 12.7553 22.274 12.6538C22.3163 12.5522 22.3381 12.4433 22.3381 12.3333C22.3381 12.2233 22.3163 12.1144 22.274 12.0128C22.2317 11.9113 22.1697 11.8191 22.0916 11.7416L19.5916 9.24162C19.5141 9.16351 19.422 9.10151 19.3204 9.05921C19.2189 9.0169 19.11 8.99512 19 8.99512C18.8899 8.99512 18.781 9.0169 18.6795 9.05921C18.5779 9.10151 18.4858 9.16351 18.4083 9.24162L15.9083 11.7416C15.7514 11.8985 15.6632 12.1114 15.6632 12.3333C15.6632 12.5552 15.7514 12.768 15.9083 12.9249C16.0652 13.0819 16.278 13.17 16.5 13.17C16.7219 13.17 17.0916 13.1714 17.0916 13.1714ZM20.9083 15.6666H19H17.0916C16.9165 15.6666 16.9165 15.6632 16.8202 15.6632C16.4998 15.6632 16.6098 15.6632 16.5 15.6632C16.3901 15.6632 16.2813 15.6848 16.1797 15.7269C16.0782 15.7689 15.986 15.8306 15.9083 15.9083C15.8306 15.986 15.769 16.0782 15.7269 16.1797C15.6849 16.2813 15.6632 16.3901 15.6632 16.4999C15.6632 16.6098 15.6849 16.7186 15.7269 16.8202C15.769 16.9217 15.8306 17.0139 15.9083 17.0916L18.4083 19.5916C18.4858 19.6697 18.5779 19.7317 18.6795 19.774C18.781 19.8163 18.8899 19.8381 19 19.8381C19.11 19.8381 19.2189 19.8163 19.3204 19.774C19.422 19.7317 19.5141 19.6697 19.5916 19.5916L22.0916 17.0916C22.2485 16.9347 22.3367 16.7219 22.3367 16.4999C22.3367 16.278 22.2485 16.0652 22.0916 15.9083C21.9347 15.7514 21.7219 15.6632 21.5 15.6632C21.278 15.6632 21.0831 15.6666 20.9083 15.6666Z' fill='white' fill-opacity='0.9'/%3E%3C/svg%3E%0A"); + +// +// Form Control/Textarea. +// +$ct-textarea-border-radius: $ct-border-radius !default; +$ct-textarea-outline-width: 2px !default; +$ct-textarea-outline-offset: 2px !default; +$ct-textarea-disabled-opacity: 40% !default; +$ct-textarea-light-background-color: ct-color-light('background-light') !default; +$ct-textarea-light-border-color: ct-color-light('border') !default; +$ct-textarea-light-color: ct-color-light('body') !default; +$ct-textarea-light-hover-background-color: $ct-textarea-light-background-color !default; +$ct-textarea-light-hover-border-color: ct-color-light('interaction-hover-background') !default; +$ct-textarea-light-hover-color: $ct-textarea-light-color !default; +$ct-textarea-light-focus-background-color: $ct-textarea-light-background-color !default; +$ct-textarea-light-focus-border-color: ct-color-light('interaction-background') !default; +$ct-textarea-light-focus-color: $ct-textarea-light-color !default; +$ct-textarea-light-invalid-background-color: ct-color-light('background-light') !default; +$ct-textarea-light-invalid-border-color: ct-color-light('error') !default; +$ct-textarea-light-invalid-color: ct-color-light('body') !default; +$ct-textarea-light-outline-color: $ct-outline-light !default; +$ct-textarea-dark-background-color: ct-color-dark('background-dark') !default; +$ct-textarea-dark-border-color: ct-color-dark('border-light') !default; +$ct-textarea-dark-color: ct-color-dark('body') !default; +$ct-textarea-dark-hover-background-color: $ct-textarea-dark-background-color !default; +$ct-textarea-dark-hover-border-color: ct-color-dark('interaction-hover-background') !default; +$ct-textarea-dark-hover-color: $ct-textarea-dark-color !default; +$ct-textarea-dark-focus-background-color: ct-color-dark('background') !default; +$ct-textarea-dark-focus-border-color: ct-color-dark('interaction-background') !default; +$ct-textarea-dark-focus-color: $ct-textarea-dark-color !default; +$ct-textarea-dark-invalid-background-color: ct-color-dark('background-dark') !default; +$ct-textarea-dark-invalid-border-color: ct-color-dark('error') !default; +$ct-textarea-dark-invalid-color: ct-color-dark('body') !default; +$ct-textarea-dark-outline-color: $ct-outline-dark !default; + +// +// Form Control/Textfield. +// +$ct-textfield-border-radius: $ct-border-radius !default; +$ct-textfield-outline-width: 2px !default; +$ct-textfield-outline-offset: 2px !default; +$ct-textfield-disabled-opacity: 40% !default; +$ct-textfield-light-background-color: ct-color-light('background-light') !default; +$ct-textfield-light-border-color: ct-color-light('border') !default; +$ct-textfield-light-color: ct-color-light('body') !default; +$ct-textfield-light-hover-background-color: $ct-textfield-light-background-color !default; +$ct-textfield-light-hover-border-color: ct-color-light('interaction-hover-background') !default; +$ct-textfield-light-hover-color: $ct-textfield-light-color !default; +$ct-textfield-light-focus-background-color: $ct-textfield-light-background-color !default; +$ct-textfield-light-focus-border-color: ct-color-light('interaction-background') !default; +$ct-textfield-light-focus-color: $ct-textfield-light-color !default; +$ct-textfield-light-invalid-background-color: ct-color-light('background-light') !default; +$ct-textfield-light-invalid-border-color: ct-color-light('error') !default; +$ct-textfield-light-invalid-color: ct-color-light('body') !default; +$ct-textfield-light-outline-color: $ct-outline-light !default; +$ct-textfield-dark-background-color: ct-color-dark('background-dark') !default; +$ct-textfield-dark-border-color: ct-color-dark('border-light') !default; +$ct-textfield-dark-color: ct-color-dark('body') !default; +$ct-textfield-dark-hover-background-color: $ct-textfield-dark-background-color !default; +$ct-textfield-dark-hover-border-color: ct-color-dark('interaction-hover-background') !default; +$ct-textfield-dark-hover-color: $ct-textfield-dark-color !default; +$ct-textfield-dark-focus-background-color: ct-color-dark('background') !default; +$ct-textfield-dark-focus-border-color: ct-color-dark('interaction-background') !default; +$ct-textfield-dark-focus-color: $ct-textfield-dark-color !default; +$ct-textfield-dark-invalid-background-color: ct-color-dark('background-dark') !default; +$ct-textfield-dark-invalid-border-color: ct-color-dark('error') !default; +$ct-textfield-dark-invalid-color: ct-color-dark('body') !default; +$ct-textfield-dark-outline-color: $ct-outline-dark !default; //////////////////////////////////////////////////////////////////////////////// // MOLECULES // //////////////////////////////////////////////////////////////////////////////// +// +// Accordion. +// +$ct-accordion-button-border-radius: $ct-border-radius !default; +$ct-accordion-header-border-radius: $ct-border-radius !default; +$ct-accordion-item-border-radius: $ct-border-radius !default; +$ct-accordion-stripe-width: $ct-stripe-size !default; +$ct-accordion-space-horizontal: ct-spacing(7) !default; +$ct-accordion-light-background-color: ct-color-light('background-light') !default; +$ct-accordion-light-border-color: ct-color-light('border-light') !default; +$ct-accordion-light-color: ct-color-light('body') !default; +$ct-accordion-light-button-background-color: ct-color-light('background-light') !default; +$ct-accordion-light-button-color: ct-color-light('heading') !default; +$ct-accordion-light-icon-color: ct-color-light('interaction-background') !default; +$ct-accordion-light-stripe-background-color: ct-color-light('highlight') !default; +$ct-accordion-light-wrapper-background-color: ct-color-light('background') !default; +$ct-accordion-button-border-radius: $ct-border-radius !default; +$ct-accordion-header-border-radius: $ct-border-radius !default; +$ct-accordion-item-border-radius: $ct-border-radius !default; +$ct-accordion-dark-background-color: ct-color-dark('background-light') !default; +$ct-accordion-dark-border-color: ct-color-dark('border') !default; +$ct-accordion-dark-color: ct-color-dark('body') !default; +$ct-accordion-dark-button-background-color: ct-color-dark('background-light') !default; +$ct-accordion-dark-button-color: ct-color-dark('heading') !default; +$ct-accordion-dark-icon-color: ct-color-dark('interaction-background') !default; +$ct-accordion-dark-stripe-background-color: ct-color-dark('highlight') !default; +$ct-accordion-dark-wrapper-background-color: ct-color-dark('background-dark') !default; + // // Attachment. // @@ -459,6 +675,16 @@ $ct-attachment-dark-border-color: ct-color-dark('border') !default; $ct-attachment-dark-color: ct-color-dark('body') !default; $ct-attachment-dark-wrapper-background-color: ct-color-dark('background') !default; +// +// Back to top. +// +$ct-back-to-top-space-bottom: ct-spacing(8) !default; +$ct-back-to-top-space-right: ct-spacing(2) !default; +$ct-back-to-top-background-color: ct-color-light('interaction-background') !default; +$ct-back-to-top-border-color: $ct-back-to-top-background-color !default; +$ct-back-to-top-color: ct-color-light('interaction-text') !default; +$ct-back-to-top-outline-color: transparent !default; + // // Basic content. // @@ -468,8 +694,10 @@ $ct-basic-content-list-marker-width: ct-particle(3.5) !default; $ct-basic-content-space-horizontal: ct-spacing(7) !default; $ct-basic-content-vertical-spacing: 1.5rem !default; $ct-basic-content-light-base-color: ct-color-light('body') !default; -$ct-basic-content-light-blockquote-block: ct-color-light('body') !default; +$ct-basic-content-light-blockquote-stripe-background-color: ct-color-light('highlight') !default; $ct-basic-content-light-blockquote-color: ct-color-light('body') !default; +$ct-basic-content-light-blockquote-background-color: ct-color-light('background-light') !default; +$ct-basic-content-light-blockquote-author-color: ct-color-light('heading') !default; $ct-basic-content-light-heading-1-color: ct-color-light('heading') !default; $ct-basic-content-light-heading-2-color: ct-color-light('heading') !default; $ct-basic-content-light-heading-3-color: ct-color-light('heading') !default; @@ -482,130 +710,46 @@ $ct-basic-content-light-ul-li-color: ct-color-light('body') !default; $ct-basic-content-light-ul-li-marker-color: ct-color-light('body') !default; $ct-basic-content-light-background-color: ct-color-light('background') !default; $ct-basic-content-dark-base-color: ct-color-dark('body') !default; -$ct-basic-content-dark-blockquote-block: ct-color-dark('body') !default; +$ct-basic-content-dark-blockquote-stripe-background-color: ct-color-dark('highlight') !default; $ct-basic-content-dark-blockquote-color: ct-color-dark('body') !default; +$ct-basic-content-dark-blockquote-background-color: ct-color-dark('background-light') !default; +$ct-basic-content-dark-blockquote-author-color: ct-color-dark('heading') !default; $ct-basic-content-dark-heading-1-color: ct-color-dark('heading') !default; $ct-basic-content-dark-heading-2-color: ct-color-dark('heading') !default; $ct-basic-content-dark-heading-3-color: ct-color-dark('heading') !default; -$ct-basic-content-dark-heading-4-color: ct-color-dark('heading') !default; -$ct-basic-content-dark-heading-5-color: ct-color-dark('heading') !default; -$ct-basic-content-dark-heading-6-color: ct-color-dark('heading') !default; -$ct-basic-content-dark-ol-li-color: ct-color-dark('body') !default; -$ct-basic-content-dark-ol-li-marker-color: ct-color-dark('body') !default; -$ct-basic-content-dark-ul-li-color: ct-color-dark('body') !default; -$ct-basic-content-dark-ul-li-marker-color: ct-color-dark('body') !default; -$ct-basic-content-dark-background-color: ct-color-dark('background') !default; - -// -// Callout. -// -$ct-callout-border-radius: $ct-border-radius !default; -$ct-callout-stripe-width: $ct-stripe-size !default; -$ct-callout-light-background-color: ct-color-light('background') !default; -$ct-callout-light-stripe-background-color: ct-color-light('highlight') !default; -$ct-callout-dark-background-color: ct-color-dark('background') !default; -$ct-callout-dark-stripe-background-color: ct-color-dark('highlight') !default; - -// -// Dropdown filter. -// -$ct-dropdown-filter-zindex: 9 !default; -$ct-dropdown-filter-light-filter-options-background-color: ct-color-light('background-light') !default; -$ct-dropdown-filter-light-filter-options-border-color: transparent !default; -$ct-dropdown-filter-light-filter-text-color: ct-color-light('body') !default; -$ct-dropdown-filter-light-filter-text-icon-color: ct-color-light('body') !default; -$ct-dropdown-filter-light-inline-filter-text-color: ct-color-light('body') !default; -$ct-dropdown-filter-dark-filter-options-background-color: ct-color-dark('background') !default; -$ct-dropdown-filter-dark-filter-options-border-color: transparent !default; -$ct-dropdown-filter-dark-filter-text-color: ct-color-dark('body') !default; -$ct-dropdown-filter-dark-filter-text-icon-color: ct-color-dark('body') !default; -$ct-dropdown-filter-dark-inline-filter-text-color: ct-color-dark('body') !default; - -// -// Campaign. -// -$ct-campaign-image-border-radius: $ct-border-radius !default; -$ct-campaign-image-shadow-offset-x: ct-particle(2) !default; -$ct-campaign-image-shadow-offset-y: ct-particle(2) !default; -$ct-campaign-mobile-height: ct-particle(30) !default; -$ct-campaign-desktop-height: ct-particle(65) !default; -$ct-campaign-light-background-color: ct-color-light('background-light') !default; -$ct-campaign-light-image-shadow-color: ct-color-light('background') !default; -$ct-campaign-dark-background-color: ct-color-dark('background') !default; -$ct-campaign-dark-image-shadow-color: ct-color-dark('background-dark') !default; - -// -// Cards/Event card. -// -$ct-event-card-border-radius: $ct-border-radius !default; -$ct-event-card-image-height-desktop: ct-particle(30) !default; -$ct-event-card-image-height-mobile: ct-particle(25) !default; -$ct-event-card-image-width-desktop: auto !default; -$ct-event-card-image-width-mobile: auto !default; -$ct-event-card-stripe-height: $ct-stripe-size !default; -$ct-event-card-light-background-color: ct-color-light('background-light') !default; -$ct-event-card-light-stripe-background-color: ct-color-light('highlight') !default; -$ct-event-card-dark-background-color: ct-color-dark('background') !default; -$ct-event-card-dark-stripe-background-color: ct-color-dark('highlight') !default; - -// -// Cards/Navigation card. -// -$ct-navigation-card-border-radius: $ct-border-radius !default; -$ct-navigation-card-image-height-mobile: ct-particle(35) !default; -$ct-navigation-card-image-width-mobile: auto !default; -$ct-navigation-card-image-height-desktop: ct-particle(32) !default; -$ct-navigation-card-image-width-desktop: ct-particle(36) !default; -$ct-navigation-card-light-background-color: ct-color-light('background-light') !default; -$ct-navigation-card-dark-background-color: ct-color-dark('background') !default; - -// -// Cards/Promo card. -// -$ct-promo-card-border-radius: $ct-border-radius !default; -$ct-promo-card-image-height-desktop: ct-particle(30) !default; -$ct-promo-card-image-height-mobile: ct-particle(25) !default; -$ct-promo-card-image-width-desktop: auto !default; -$ct-promo-card-image-width-mobile: auto !default; -$ct-promo-card-stripe-height: $ct-stripe-size !default; -$ct-promo-card-light-background-color: ct-color-light('background-light') !default; -$ct-promo-card-light-stripe-background-color: ct-color-light('highlight') !default; -$ct-promo-card-dark-background-color: ct-color-dark('background') !default; -$ct-promo-card-dark-stripe-background-color: ct-color-dark('highlight') !default; +$ct-basic-content-dark-heading-4-color: ct-color-dark('heading') !default; +$ct-basic-content-dark-heading-5-color: ct-color-dark('heading') !default; +$ct-basic-content-dark-heading-6-color: ct-color-dark('heading') !default; +$ct-basic-content-dark-ol-li-color: ct-color-dark('body') !default; +$ct-basic-content-dark-ol-li-marker-color: ct-color-dark('body') !default; +$ct-basic-content-dark-ul-li-color: ct-color-dark('body') !default; +$ct-basic-content-dark-ul-li-marker-color: ct-color-dark('body') !default; +$ct-basic-content-dark-background-color: ct-color-dark('background') !default; // -// Cards/Publication card. +// Breadcrumb. // -$ct-publication-card-border-radius: $ct-border-radius !default; -$ct-publication-card-image-height-mobile: ct-particle(23) !default; -$ct-publication-card-image-width-mobile: auto !default; -$ct-publication-card-image-height-desktop: ct-particle(23) !default; -$ct-publication-card-image-width-desktop: 50% !default; -$ct-publication-card-light-background-color: ct-color-light('background-light') !default; -$ct-publication-card-dark-background-color: ct-color-dark('background') !default; +$ct-breadcrumb-light-color: ct-color-light('body') !default; +$ct-breadcrumb-light-active-color: $ct-breadcrumb-light-color !default; +$ct-breadcrumb-dark-color: ct-color-dark('body') !default; +$ct-breadcrumb-dark-active-color: $ct-breadcrumb-dark-color !default; // -// Cards/Service card. +// Callout. // -$ct-service-card-border-radius: $ct-border-radius !default; -$ct-service-card-stripe-height: $ct-stripe-size !default; -$ct-service-card-light-background-color: ct-color-light('background-light') !default; -$ct-service-card-light-stripe-background-color: ct-color-light('highlight') !default; -$ct-service-card-dark-background-color: ct-color-dark('background') !default; -$ct-service-card-dark-stripe-background-color: ct-color-dark('highlight') !default; +$ct-callout-border-radius: $ct-border-radius !default; +$ct-callout-stripe-width: $ct-stripe-size !default; +$ct-callout-light-background-color: ct-color-light('background') !default; +$ct-callout-light-stripe-background-color: ct-color-light('highlight') !default; +$ct-callout-dark-background-color: ct-color-dark('background') !default; +$ct-callout-dark-stripe-background-color: ct-color-dark('highlight') !default; // -// Cards/Subject card. +// Field. // -$ct-subject-card-border-radius: $ct-border-radius !default; -$ct-subject-card-image-height-mobile: ct-particle(20) !default; -$ct-subject-card-image-width-mobile: auto !default; -$ct-subject-card-image-height-desktop: ct-particle(28) !default; -$ct-subject-card-image-width-desktop: auto !default; -$ct-subject-card-light-background-color: ct-color-light('background-light') !default; -$ct-subject-card-light-image-opacity: 30% !default; -$ct-subject-card-dark-background-color: ct-color-dark('background') !default; -$ct-subject-card-dark-image-opacity: 20% !default; +$ct-field-horizontal-label-min-width: auto !default; +$ct-field-horizontal-label-max-width: 25% !default; +$ct-field-label-fixed-widths: 15, 25 !default; // // Figure. @@ -614,48 +758,6 @@ $ct-figure-border-radius: $ct-border-radius !default; $ct-figure-light-caption-color: ct-color-light('body') !default; $ct-figure-dark-caption-color: ct-color-dark('body') !default; -// -// Form/Form element. -// -$ct-form-element-light-color: ct-color-light('body') !default; -$ct-form-element-dark-color: ct-color-dark('body') !default; - -// -// Single filter. -// -$ct-single-filter-light-border-color: ct-color-light('border-light') !default; -$ct-single-filter-dark-border-color: ct-color-dark('border') !default; - -// -// Group filter. -// -$ct-group-filter-light-background-color: ct-color-light('background') !default; -$ct-group-filter-light-border-color: ct-color-light('border-light') !default; -$ct-group-filter-light-dropdown-filter-border-color: ct-color-light('border') !default; -$ct-group-filter-light-filters-legend-color: ct-color-light('body') !default; -$ct-group-filter-light-mobile-filter-title-color: ct-color-light('background') !default; -$ct-group-filter-light-mobile-overlay-background-color: ct-color-light('background') !default; -$ct-group-filter-light-mobile-toggle-background-color: ct-color-light('background') !default; -$ct-group-filter-light-mobile-toggle-border-color: ct-color-light('border') !default; -$ct-group-filter-light-mobile-toggle-color: ct-color-light('interaction-background') !default; -$ct-group-filter-light-mobile-toggle-display-border-color: ct-color-light('border') !default; -$ct-group-filter-light-mobile-toggle-display-color: ct-color-light('background') !default; -$ct-group-filter-light-selected-filters-border-color: ct-color-light('border-light') !default; -$ct-group-filter-light-selected-filters-title-color: ct-color-light('body') !default; -$ct-group-filter-dark-background-color: ct-color-dark('background') !default; -$ct-group-filter-dark-border-color: ct-color-dark('border') !default; -$ct-group-filter-dark-dropdown-filter-border-color: ct-color-dark('interaction-background') !default; -$ct-group-filter-dark-filters-legend-color: ct-color-dark('body') !default; -$ct-group-filter-dark-mobile-filter-title-color: ct-color-dark('body') !default; -$ct-group-filter-dark-mobile-overlay-background-color: ct-color-dark('background') !default; -$ct-group-filter-dark-mobile-toggle-background-color: ct-color-dark('background') !default; -$ct-group-filter-dark-mobile-toggle-border-color: ct-color-dark('interaction-background') !default; -$ct-group-filter-dark-mobile-toggle-color: ct-color-dark('background') !default; -$ct-group-filter-dark-mobile-toggle-display-border-color: ct-color-dark('interaction-background') !default; -$ct-group-filter-dark-mobile-toggle-display-color: ct-color-dark('background') !default; -$ct-group-filter-dark-selected-filters-border-color: ct-color-dark('border-light') !default; -$ct-group-filter-dark-selected-filters-title-color: ct-color-dark('background') !default; - // // Logo. // @@ -685,21 +787,6 @@ $ct-next-step-dark-background-color: ct-color-dark('background') !default; $ct-next-step-dark-border-color: ct-color-dark('border') !default; $ct-next-step-dark-stripe-background-color: ct-color-dark('highlight') !default; -// -// Quote. -// -$ct-quote-border-width: ct-particle(0.75) !default; -$ct-quote-light-background-color: ct-color-light('background-light') !default; -$ct-quote-light-border-color: ct-color-light('highlight') !default; -$ct-quote-light-body-color: ct-color-light('body') !default; -$ct-quote-light-author-color: ct-color-light('heading') !default; -$ct-quote-light-stripe-background-color: ct-color-light('highlight') !default; -$ct-quote-dark-background-color: ct-color-dark('background-light') !default; -$ct-quote-dark-border-color: ct-color-dark('highlight') !default; -$ct-quote-dark-body-color: ct-color-dark('body') !default; -$ct-quote-dark-author-color: ct-color-dark('heading') !default; -$ct-quote-dark-stripe-background-color: ct-color-dark('highlight') !default; - // // Table Of Contents. // @@ -742,20 +829,6 @@ $ct-tabs-dark-links-active-border-color: ct-color-dark('border-light') !default; $ct-tabs-dark-panel-background-color: ct-color-dark('background') !default; $ct-tabs-dark-panel-border-color: ct-color-dark('border') !default; -// -// Breadcrumb. -// -$ct-breadcrumb-light-color: ct-color-light('body') !default; -$ct-breadcrumb-light-active-color: $ct-breadcrumb-light-color !default; -$ct-breadcrumb-dark-color: ct-color-dark('body') !default; -$ct-breadcrumb-dark-active-color: $ct-breadcrumb-dark-color !default; - -// -// Snippet. -// -$ct-snippet-light-background-color: transparent !default; -$ct-snippet-dark-background-color: transparent !default; - // // Tooltip. // @@ -772,77 +845,167 @@ $ct-tooltip-dark-description-background-color: ct-color-dark('interaction-backgr $ct-tooltip-dark-description-color: ct-color-dark('interaction-text') !default; $ct-tooltip-dark-icon-color: ct-color-dark('interaction-background') !default; -//////////////////////////////////////////////////////////////////////////////// -// ORGANISMS // -//////////////////////////////////////////////////////////////////////////////// +// +// List/Single filter. +// +$ct-single-filter-light-border-color: ct-color-light('border-light') !default; +$ct-single-filter-dark-border-color: ct-color-dark('border') !default; // -// Accordion. +// List/Group filter. // -$ct-accordion-button-border-radius: $ct-border-radius !default; -$ct-accordion-header-border-radius: $ct-border-radius !default; -$ct-accordion-item-border-radius: $ct-border-radius !default; -$ct-accordion-stripe-width: $ct-stripe-size !default; -$ct-accordion-space-horizontal: ct-spacing(7) !default; -$ct-accordion-light-background-color: ct-color-light('background-light') !default; -$ct-accordion-light-border-color: ct-color-light('border-light') !default; -$ct-accordion-light-color: ct-color-light('body') !default; -$ct-accordion-light-button-background-color: ct-color-light('background-light') !default; -$ct-accordion-light-button-color: ct-color-light('heading') !default; -$ct-accordion-light-icon-color: ct-color-light('interaction-background') !default; -$ct-accordion-light-stripe-background-color: ct-color-light('highlight') !default; -$ct-accordion-light-wrapper-background-color: ct-color-light('background') !default; -$ct-accordion-button-border-radius: $ct-border-radius !default; -$ct-accordion-header-border-radius: $ct-border-radius !default; -$ct-accordion-item-border-radius: $ct-border-radius !default; -$ct-accordion-dark-background-color: ct-color-dark('background-light') !default; -$ct-accordion-dark-border-color: ct-color-dark('border') !default; -$ct-accordion-dark-color: ct-color-dark('body') !default; -$ct-accordion-dark-button-background-color: ct-color-dark('background-light') !default; -$ct-accordion-dark-button-color: ct-color-dark('heading') !default; -$ct-accordion-dark-icon-color: ct-color-dark('interaction-background') !default; -$ct-accordion-dark-stripe-background-color: ct-color-dark('highlight') !default; -$ct-accordion-dark-wrapper-background-color: ct-color-dark('background-dark') !default; +$ct-group-filter-light-background-color: ct-color-light('background') !default; +$ct-group-filter-light-border-color: ct-color-light('border-light') !default; +$ct-group-filter-light-dropdown-filter-border-color: ct-color-light('border') !default; +$ct-group-filter-light-filters-legend-color: ct-color-light('body') !default; +$ct-group-filter-light-mobile-filter-title-color: ct-color-light('background') !default; +$ct-group-filter-light-mobile-overlay-background-color: ct-color-light('background') !default; +$ct-group-filter-light-mobile-toggle-background-color: ct-color-light('background') !default; +$ct-group-filter-light-mobile-toggle-border-color: ct-color-light('border') !default; +$ct-group-filter-light-mobile-toggle-color: ct-color-light('interaction-background') !default; +$ct-group-filter-light-mobile-toggle-display-border-color: ct-color-light('border') !default; +$ct-group-filter-light-mobile-toggle-display-color: ct-color-light('background') !default; +$ct-group-filter-light-selected-filters-border-color: ct-color-light('border-light') !default; +$ct-group-filter-light-selected-filters-title-color: ct-color-light('body') !default; +$ct-group-filter-dark-background-color: ct-color-dark('background') !default; +$ct-group-filter-dark-border-color: ct-color-dark('border') !default; +$ct-group-filter-dark-dropdown-filter-border-color: ct-color-dark('interaction-background') !default; +$ct-group-filter-dark-filters-legend-color: ct-color-dark('body') !default; +$ct-group-filter-dark-mobile-filter-title-color: ct-color-dark('body') !default; +$ct-group-filter-dark-mobile-overlay-background-color: ct-color-dark('background') !default; +$ct-group-filter-dark-mobile-toggle-background-color: ct-color-dark('background') !default; +$ct-group-filter-dark-mobile-toggle-border-color: ct-color-dark('interaction-background') !default; +$ct-group-filter-dark-mobile-toggle-color: ct-color-dark('background') !default; +$ct-group-filter-dark-mobile-toggle-display-border-color: ct-color-dark('interaction-background') !default; +$ct-group-filter-dark-mobile-toggle-display-color: ct-color-dark('background') !default; +$ct-group-filter-dark-selected-filters-border-color: ct-color-dark('border-light') !default; +$ct-group-filter-dark-selected-filters-title-color: ct-color-dark('background') !default; + +// +// List/Pagination. +// +$ct-pagination-light-color: ct-color-light('interaction-background') !default; +$ct-pagination-light-border-color: ct-color-light('border-light') !default; +$ct-pagination-dark-color: ct-color-dark('interaction-background') !default; +$ct-pagination-dark-border-color: ct-color-dark('border') !default; + +// +// List/Event card. +// +$ct-event-card-border-radius: $ct-border-radius !default; +$ct-event-card-image-height-desktop: ct-particle(30) !default; +$ct-event-card-image-height-mobile: ct-particle(25) !default; +$ct-event-card-image-width-desktop: auto !default; +$ct-event-card-image-width-mobile: auto !default; +$ct-event-card-stripe-height: $ct-stripe-size !default; +$ct-event-card-light-background-color: ct-color-light('background-light') !default; +$ct-event-card-light-stripe-background-color: ct-color-light('highlight') !default; +$ct-event-card-dark-background-color: ct-color-dark('background') !default; +$ct-event-card-dark-stripe-background-color: ct-color-dark('highlight') !default; + +// +// List/Navigation card. +// +$ct-navigation-card-border-radius: $ct-border-radius !default; +$ct-navigation-card-image-height-mobile: ct-particle(35) !default; +$ct-navigation-card-image-width-mobile: auto !default; +$ct-navigation-card-image-min-height-desktop: ct-particle(32) !default; +$ct-navigation-card-image-min-width-desktop: ct-particle(36) !default; +$ct-navigation-card-image-max-width-desktop: 30% !default; +$ct-navigation-card-orientation-threshold: $ct-navigation-card-image-min-width-desktop * 2 !default; +$ct-navigation-card-light-background-color: ct-color-light('background-light') !default; +$ct-navigation-card-light-icon-color: $ct-link-light-color !default; +$ct-navigation-card-dark-background-color: ct-color-dark('background') !default; +$ct-navigation-card-dark-icon-color: $ct-link-dark-color !default; + +// +// List/Promo card. +// +$ct-promo-card-border-radius: $ct-border-radius !default; +$ct-promo-card-image-height-desktop: ct-particle(30) !default; +$ct-promo-card-image-height-mobile: ct-particle(25) !default; +$ct-promo-card-image-width-desktop: auto !default; +$ct-promo-card-image-width-mobile: auto !default; +$ct-promo-card-stripe-height: $ct-stripe-size !default; +$ct-promo-card-light-background-color: ct-color-light('background-light') !default; +$ct-promo-card-light-stripe-background-color: ct-color-light('highlight') !default; +$ct-promo-card-dark-background-color: ct-color-dark('background') !default; +$ct-promo-card-dark-stripe-background-color: ct-color-dark('highlight') !default; + +// +// List/Publication card. +// +$ct-publication-card-border-radius: $ct-border-radius !default; +$ct-publication-card-image-height-mobile: ct-particle(23) !default; +$ct-publication-card-image-width-mobile: auto !default; +$ct-publication-card-image-height-desktop: ct-particle(23) !default; +$ct-publication-card-image-width-desktop: 50% !default; +$ct-publication-card-light-background-color: ct-color-light('background-light') !default; +$ct-publication-card-dark-background-color: ct-color-dark('background') !default; + +// +// List/Service card. +// +$ct-service-card-border-radius: $ct-border-radius !default; +$ct-service-card-stripe-height: $ct-stripe-size !default; +$ct-service-card-light-background-color: ct-color-light('background-light') !default; +$ct-service-card-light-stripe-background-color: ct-color-light('highlight') !default; +$ct-service-card-dark-background-color: ct-color-dark('background') !default; +$ct-service-card-dark-stripe-background-color: ct-color-dark('highlight') !default; + +// +// List/Subject card. +// +$ct-subject-card-border-radius: $ct-border-radius !default; +$ct-subject-card-image-height-mobile: ct-particle(20) !default; +$ct-subject-card-image-width-mobile: auto !default; +$ct-subject-card-image-height-desktop: ct-particle(28) !default; +$ct-subject-card-image-width-desktop: auto !default; +$ct-subject-card-light-background-color: ct-color-light('background-light') !default; +$ct-subject-card-light-image-opacity: 30% !default; +$ct-subject-card-dark-background-color: ct-color-dark('background') !default; +$ct-subject-card-dark-image-opacity: 20% !default; + +// +// List/Snippet. +// +$ct-snippet-light-background-color: transparent !default; +$ct-snippet-dark-background-color: transparent !default; + +//////////////////////////////////////////////////////////////////////////////// +// ORGANISMS // +//////////////////////////////////////////////////////////////////////////////// // // Alert. // $ct-alert-light-divider-color: ct-color-light('interaction-background') !default; -$ct-alert-light-error-background-color: ct-color-tint(ct-color-constant-light('error'), 90) !default; +$ct-alert-light-error-background-color: ct-color-tint(ct-color-constant-light('error'), 80) !default; $ct-alert-light-error-color: ct-color-light('body') !default; $ct-alert-light-error-icon-color: $ct-alert-light-error-color !default; -$ct-alert-light-information-background-color: ct-color-tint(ct-color-constant-light('information'), 90) !default; +$ct-alert-light-information-background-color: ct-color-tint(ct-color-constant-light('information'), 80) !default; $ct-alert-light-information-color: ct-color-light('body') !default; $ct-alert-light-information-icon-color: $ct-alert-light-information-color !default; -$ct-alert-light-success-background-color: ct-color-tint(ct-color-constant-light('success'), 90) !default; +$ct-alert-light-success-background-color: ct-color-tint(ct-color-constant-light('success'), 80) !default; $ct-alert-light-success-color: ct-color-light('body') !default; $ct-alert-light-success-icon-color: $ct-alert-light-success-color !default; -$ct-alert-light-warning-background-color: ct-color-tint(ct-color-constant-light('warning'), 90) !default; +$ct-alert-light-warning-background-color: ct-color-tint(ct-color-constant-light('warning'), 80) !default; $ct-alert-light-warning-color: ct-color-light('body') !default; $ct-alert-light-warning-icon-color: $ct-alert-light-warning-color !default; $ct-alert-dark-divider-color: ct-color-dark('interaction-background') !default; -$ct-alert-dark-error-background-color: ct-color-shade(ct-color-constant-dark('error'), 30) !default; +$ct-alert-dark-error-background-color: ct-color-shade(ct-color-constant-dark('error'), 20) !default; $ct-alert-dark-error-color: ct-color-dark('body') !default; $ct-alert-dark-error-icon-color: $ct-alert-dark-error-color !default; -$ct-alert-dark-information-background-color: ct-color-shade(ct-color-constant-dark('information'), 30) !default; +$ct-alert-dark-information-background-color: ct-color-shade(ct-color-constant-dark('information'), 20) !default; $ct-alert-dark-information-color: ct-color-dark('body') !default; $ct-alert-dark-information-icon-color: $ct-alert-dark-information-color !default; -$ct-alert-dark-success-background-color: ct-color-shade(ct-color-constant-dark('success'), 30) !default; +$ct-alert-dark-success-background-color: ct-color-shade(ct-color-constant-dark('success'), 20) !default; $ct-alert-dark-success-color: ct-color-dark('body') !default; $ct-alert-dark-success-icon-color: $ct-alert-dark-success-color !default; -$ct-alert-dark-warning-background-color: ct-color-shade(ct-color-constant-dark('warning'), 30) !default; +$ct-alert-dark-warning-background-color: ct-color-shade(ct-color-constant-dark('warning'), 20) !default; $ct-alert-dark-warning-color: ct-color-dark('body') !default; $ct-alert-dark-warning-icon-color: $ct-alert-dark-warning-color !default; -// -// Back to top. -// -$ct-back-to-top-space-bottom: ct-spacing(8) !default; -$ct-back-to-top-space-right: ct-spacing(2) !default; -$ct-back-to-top-background-color: ct-color-light('interaction-background') !default; -$ct-back-to-top-color: ct-color-light('interaction-text') !default; -$ct-back-to-top-outline-color: transparent !default; - // // Banner. // @@ -856,16 +1019,16 @@ $ct-banner-dark-background-color: ct-color-dark('background') !default; $ct-banner-dark-featured-image-shadow-color: ct-color-dark('background') !default; // -// Layout. -// -$ct-layout-light-background-color: inherit !default; -$ct-layout-dark-background-color: inherit !default; - -// -// Form/Fieldset. +// Campaign. // -$ct-fieldset-light-required-color: ct-color-light('error') !default; -$ct-fieldset-dark-required-color: ct-color-dark('error') !default; +$ct-campaign-image-border-radius: $ct-border-radius !default; +$ct-campaign-image-shadow-offset-x: ct-particle(2) !default; +$ct-campaign-image-shadow-offset-y: ct-particle(2) !default; +$ct-campaign-mobile-height: ct-particle(30) !default; +$ct-campaign-light-background-color: ct-color-light('background-light') !default; +$ct-campaign-light-image-shadow-color: ct-color-light('background') !default; +$ct-campaign-dark-background-color: ct-color-dark('background') !default; +$ct-campaign-dark-image-shadow-color: ct-color-dark('background-dark') !default; // // Footer. @@ -891,7 +1054,7 @@ $ct-header-dark-top-background-color: ct-color-dark('background-dark') !default; $ct-header-dark-top-color: ct-color-dark('body') !default; // -// Listing. +// List. // $ct-list-light-background-color: ct-color-light('background') !default; $ct-list-light-color: ct-color-light('body') !default; @@ -902,35 +1065,35 @@ $ct-list-dark-color: ct-color-dark('body') !default; // Message. // $ct-message-border-radius: $ct-border-radius !default; -$ct-message-light-error-background-color: ct-color-tint(ct-color-constant-light('error'), 90) !default; +$ct-message-light-error-background-color: ct-color-tint(ct-color-constant-light('error'), 80) !default; $ct-message-light-error-border-color: ct-color-light('error') !default; $ct-message-light-error-color: ct-color-light('body') !default; $ct-message-light-error-icon-color: $ct-message-light-error-color !default; -$ct-message-light-information-background-color: ct-color-tint(ct-color-constant-light('information'), 90) !default; +$ct-message-light-information-background-color: ct-color-tint(ct-color-constant-light('information'), 80) !default; $ct-message-light-information-border-color: ct-color-light('information') !default; $ct-message-light-information-color: ct-color-light('body') !default; $ct-message-light-information-icon-color: $ct-message-light-information-color !default; $ct-message-light-success-border-color: ct-color-light('success') !default; -$ct-message-light-success-background-color: ct-color-tint(ct-color-constant-light('success'), 90) !default; +$ct-message-light-success-background-color: ct-color-tint(ct-color-constant-light('success'), 80) !default; $ct-message-light-success-color: ct-color-light('body') !default; $ct-message-light-success-icon-color: $ct-message-light-success-color !default; -$ct-message-light-warning-background-color: ct-color-tint(ct-color-constant-light('warning'), 90) !default; +$ct-message-light-warning-background-color: ct-color-tint(ct-color-constant-light('warning'), 80) !default; $ct-message-light-warning-border-color: ct-color-light('warning') !default; $ct-message-light-warning-color: ct-color-light('body') !default; $ct-message-light-warning-icon-color: $ct-message-light-warning-color !default; -$ct-message-dark-error-background-color: ct-color-shade(ct-color-constant-dark('error'), 30) !default; +$ct-message-dark-error-background-color: ct-color-shade(ct-color-constant-dark('error'), 20) !default; $ct-message-dark-error-border-color: ct-color-dark('error') !default; $ct-message-dark-error-color: ct-color-dark('body') !default; $ct-message-dark-error-icon-color: $ct-message-dark-error-color !default; -$ct-message-dark-information-background-color: ct-color-shade(ct-color-constant-dark('information'), 30) !default; +$ct-message-dark-information-background-color: ct-color-shade(ct-color-constant-dark('information'), 20) !default; $ct-message-dark-information-border-color: ct-color-dark('information') !default; $ct-message-dark-information-color: ct-color-dark('body') !default; $ct-message-dark-information-icon-color: $ct-message-dark-information-color !default; $ct-message-dark-success-border-color: ct-color-dark('success') !default; -$ct-message-dark-success-background-color: ct-color-shade(ct-color-constant-dark('success'), 30) !default; +$ct-message-dark-success-background-color: ct-color-shade(ct-color-constant-dark('success'), 20) !default; $ct-message-dark-success-color: ct-color-dark('body') !default; $ct-message-dark-success-icon-color: $ct-message-dark-success-color !default; -$ct-message-dark-warning-background-color: ct-color-shade(ct-color-constant-dark('warning'), 30) !default; +$ct-message-dark-warning-background-color: ct-color-shade(ct-color-constant-dark('warning'), 20) !default; $ct-message-dark-warning-border-color: ct-color-dark('warning') !default; $ct-message-dark-warning-color: ct-color-dark('body') !default; $ct-message-dark-warning-icon-color: $ct-message-dark-warning-color !default; @@ -940,6 +1103,10 @@ $ct-message-dark-warning-icon-color: $ct-message-dark-warning-color !default; // // Navigation can be rendered in 3 ways: plain, with dropdown and with drawer. +// Inline. +$ct-navigation-inline-column-gutter: $ct-item-list-horizontal-regular-column-gap !default; +$ct-navigation-inline-row-gutter: $ct-item-list-horizontal-regular-column-gap !default; + // Dropdown. $ct-navigation-dropdown-zindex: 11 !default; $ct-navigation-dropdown-border-radius: $ct-border-radius !default; @@ -964,6 +1131,18 @@ $ct-navigation-drawer-top-offset: ct-spacing(2) !default; $ct-navigation-drawer-column-gutter: ct-spacing(4) !default; // Colors. +$ct-navigation-light-menu-border-color: ct-color-light('interaction-background') !default; +$ct-navigation-light-menu-color: ct-color-light('interaction-background') !default; +$ct-navigation-light-menu-hover-color: ct-color-light('interaction-hover-background') !default; +$ct-navigation-light-menu-active-color: ct-color-light('interaction-hover-background') !default; +$ct-navigation-light-menu-item-background-color: transparent !default; +$ct-navigation-light-menu-item-border-color: transparent !default; +$ct-navigation-light-menu-item-hover-background-color: transparent !default; +$ct-navigation-light-menu-item-hover-border-color: ct-color-light('interaction-hover-background') !default; +$ct-navigation-light-menu-item-active-background-color: transparent !default; +$ct-navigation-light-menu-item-active-border-color: ct-color-light('highlight') !default; +$ct-navigation-light-menu-item-active-trail-background-color: ct-color-light('interaction-background') !default; +$ct-navigation-light-menu-item-active-trail-color: ct-color-light('background') !default; $ct-navigation-light-drawer-border-color: ct-color-light('interaction-background') !default; $ct-navigation-light-drawer-color: ct-color-light('interaction-background') !default; $ct-navigation-light-drawer-hover-color: ct-color-light('interaction-hover-background') !default; @@ -986,6 +1165,18 @@ $ct-navigation-light-drawer-sub-menu-item-hover-background-color: ct-color-light $ct-navigation-light-drawer-sub-menu-item-hover-color: ct-color-light('interaction-hover-text') !default; $ct-navigation-light-drawer-sub-menu-item-active-background-color: ct-color-light('body') !default; $ct-navigation-light-drawer-sub-menu-item-active-color: ct-color-light('background') !default; +$ct-navigation-dark-menu-border-color: ct-color-dark('interaction-background') !default; +$ct-navigation-dark-menu-color: ct-color-dark('interaction-background') !default; +$ct-navigation-dark-menu-hover-color: ct-color-dark('interaction-hover-background') !default; +$ct-navigation-dark-menu-active-color: ct-color-dark('interaction-hover-background') !default; +$ct-navigation-dark-menu-item-background-color: transparent !default; +$ct-navigation-dark-menu-item-border-color: transparent !default; +$ct-navigation-dark-menu-item-hover-background-color: transparent !default; +$ct-navigation-dark-menu-item-hover-border-color: ct-color-dark('interaction-hover-background') !default; +$ct-navigation-dark-menu-item-active-background-color: transparent !default; +$ct-navigation-dark-menu-item-active-border-color: ct-color-dark('highlight') !default; +$ct-navigation-dark-menu-item-active-trail-background-color: ct-color-dark('interaction-background') !default; +$ct-navigation-dark-menu-item-active-trail-color: ct-color-dark('background') !default; $ct-navigation-dark-drawer-border-color: ct-color-dark('interaction-background') !default; $ct-navigation-dark-drawer-color: ct-color-dark('interaction-background') !default; $ct-navigation-dark-drawer-hover-color: ct-color-dark('interaction-hover-background') !default; @@ -1041,7 +1232,7 @@ $ct-side-navigation-light-link-child-hover-stripe-background-color: ct-color-lig $ct-side-navigation-light-link-child-active-background-color: ct-color-light('background') !default; $ct-side-navigation-light-link-child-active-border-color: $ct-side-navigation-light-link-child-border-color !default; $ct-side-navigation-light-link-child-active-color: $ct-side-navigation-light-link-child-color !default; -$ct-side-navigation-light-link-child-active-stripe-background-color: $ct-side-navigation-light-link-child-stripe-background-color !default; +$ct-side-navigation-light-link-child-active-stripe-background-color: $ct-side-navigation-light-link-child-hover-stripe-background-color !default; $ct-side-navigation-dark-title-color: ct-color-dark('heading') !default; $ct-side-navigation-dark-link-expanded-icon-color: ct-color-dark('heading') !default; $ct-side-navigation-dark-link-parent-background-color: ct-color-dark('background-light') !default; @@ -1067,10 +1258,10 @@ $ct-side-navigation-dark-link-child-hover-stripe-background-color: ct-color-dark $ct-side-navigation-dark-link-child-active-background-color: ct-color-dark('background') !default; $ct-side-navigation-dark-link-child-active-border-color: $ct-side-navigation-dark-link-child-border-color !default; $ct-side-navigation-dark-link-child-active-color: $ct-side-navigation-dark-link-child-color !default; -$ct-side-navigation-dark-link-child-active-stripe-background-color: $ct-side-navigation-dark-link-child-stripe-background-color !default; +$ct-side-navigation-dark-link-child-active-stripe-background-color: $ct-side-navigation-dark-link-child-hover-stripe-background-color !default; // -// Mobile navigation. +// Navigation/Mobile navigation. // $ct-mobile-navigation-breakpoint: $ct-header-desktop-menu-breakpoint !default; $ct-mobile-navigation-light-panel-background-color: ct-color-light('background-light') !default; @@ -1101,6 +1292,7 @@ $ct-promo-light-background-color: ct-color-light('background-light') !default; $ct-promo-light-border-color: ct-color-light('border-light') !default; $ct-promo-dark-background-color: ct-color-dark('background') !default; $ct-promo-dark-border-color: ct-color-dark('border') !default; +$ct-promo-space-horizontal: ct-spacing(7) !default; // // Skip link. @@ -1130,9 +1322,7 @@ $ct-webform-light-background-color: ct-color-light('background') !default; $ct-webform-dark-background-color: ct-color-dark('background') !default; // -// Pagination. +// Page. // -$ct-pager-light-color: ct-color-light('interaction-background') !default; -$ct-pager-light-border-color: ct-color-light('border-light') !default; -$ct-pager-dark-color: ct-color-dark('interaction-background') !default; -$ct-pager-dark-border-color: ct-color-dark('border') !default; +$ct-page-light-background-color: ct-color-light('background-light') !default; +$ct-page-dark-background-color: ct-color-dark('background-dark') !default; diff --git a/components/00-base/about-civictheme/about-civictheme.stories.js b/components/00-base/about-civictheme/about-civictheme.stories.js new file mode 100644 index 00000000..f350c160 --- /dev/null +++ b/components/00-base/about-civictheme/about-civictheme.stories.js @@ -0,0 +1,25 @@ +import AboutCivicThemeStoryTemplate from './about-civictheme.stories.twig'; + +export default { + title: 'About CivicTheme', + parameters: { + layout: 'fullscreen', + options: { showPanel: false }, + showPanel: false, + }, +}; + +export const AboutCivicTheme = () => AboutCivicThemeStoryTemplate({ + logos: { + primary: { + mobile: { + url: LOGOS.light.civictheme.mobile, + }, + desktop: { + url: LOGOS.light.civictheme.desktop, + }, + }, + }, +}); + +AboutCivicTheme.storyName = 'About CivicTheme'; diff --git a/components/00-base/about-civictheme/about-civictheme.stories.scss b/components/00-base/about-civictheme/about-civictheme.stories.scss new file mode 100644 index 00000000..d96c9fd5 --- /dev/null +++ b/components/00-base/about-civictheme/about-civictheme.stories.scss @@ -0,0 +1,37 @@ +.story-about-civictheme { + // CivicTheme's own brand color. + background-color: #003f56; + + .ct-logo { + .ct-logo__image--mobile { + max-height: ct-particle(6); + } + + .ct-logo__image--desktop { + max-height: ct-particle(12); + } + } + + .ct-heading { + margin-top: ct-spacing(3); + + @include ct-breakpoint(m) { + margin-top: 0; + } + } + + .ct-item-list__item { + display: flex; + padding-left: 0; + align-items: center; + + .ct-paragraph { + padding-left: ct-spacing(2); + margin-bottom: 0; + } + + .ct-icon { + overflow: visible; + } + } +} diff --git a/components/00-base/about-civictheme/about-civictheme.stories.twig b/components/00-base/about-civictheme/about-civictheme.stories.twig new file mode 100644 index 00000000..25c64f97 --- /dev/null +++ b/components/00-base/about-civictheme/about-civictheme.stories.twig @@ -0,0 +1,219 @@ +{# +/** + * @file + * About CivicTheme story template. + */ +#} + +
+
+
+
+ {% include '@molecules/logo/logo.twig' with { + theme: 'light', + logos: logos, + url: 'https://civictheme.io/', + } only %} +
+
+
+
+
+
+
+ {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'CivicTheme is an open-source design system build for modern web development. It offers a comprehensive suite of customisable components and templates designed to streamline the creation of visually appealing and highly functional websites.', + } only %} + + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'With a strong emphasis on accessibility compliance, CivicTheme ensures compliance with WCAG 2.2 standards and the platform\'s responsive design provides a seamless user experience across various devices and screen sizes. The rich library of pre-built components, such as buttons, forms, and navigation menus, allow developers to quickly integrate essential features into their projects, significantly reducing development time and costs.', + } only %} + +
+
+ {% include '@atoms/heading/heading.twig' with { + theme: 'dark', + content: 'Key benefits', + level: 3, + } only %} + + {% set list_item1 %} + {% include '@base/icon/icon.twig' with { + symbol: 'layer', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: list_item_icon_1 ~ ' Atomic design system of 60+ components', + allow_html: true, + } only %} + {% endset %} + + {% set list_item2 %} + {% include '@base/icon/icon.twig' with { + symbol: 'eye', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'Compliant with WCAG 2.2 AA Standards', + } only %} + {% endset %} + + {% set list_item3 %} + {% include '@base/icon/icon.twig' with { + symbol: 'water-drop', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'Integrates with Drupal theme', + } only %} + {% endset %} + + {% set list_item4 %} + {% include '@base/icon/icon.twig' with { + symbol: 'open-source', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'Open source and community-driven', + } only %} + {% endset %} + + {% include '@base/item-list/item-list.twig' with { + direction: 'vertical', + items: [ + list_item1, + list_item2, + list_item3, + list_item4, + ], + } only %} +
+ +
+ + {% include '@atoms/heading/heading.twig' with { + theme: 'dark', + content: 'Key features', + level: 3, + } only %} + + {% set list_item1 %} + {% include '@base/icon/icon.twig' with { + symbol: 'web', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'Enhanced user experience', + } only %} + {% endset %} + + {% set list_item2 %} + {% include '@base/icon/icon.twig' with { + symbol: 'mobile', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'Responsive design and consistent branding', + } only %} + {% endset %} + + {% set list_item3 %} + {% include '@base/icon/icon.twig' with { + symbol: 'layers', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'Scalable and flexible for evolving needs', + } only %} + {% endset %} + + {% set list_item4 %} + {% include '@base/icon/icon.twig' with { + symbol: 'document', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'Documentation and support', + } only %} + {% endset %} + + {% include '@base/item-list/item-list.twig' with { + direction: 'vertical', + items: [ + list_item1, + list_item2, + list_item3, + list_item4, + ], + } only %} +
+
+
+
+ +
+
+ {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'CivicTheme\'s focus on consistent branding and design aims to ensure that organisations can maintain a cohesive online presence, reinforcing their brand identity across all digital touchpoints.', + } only %} + + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'Ultimately, CivicTheme\'s blend of flexibility, scalability, and cost efficiency makes it an ideal choice for organisations seeking to create professional, user-friendly websites without the need for extensive development resources.', + } only %} +
+
+ +
+
+ {% include '@atoms/heading/heading.twig' with { + theme: 'dark', + content: 'Under the following conditions:', + level: 5, + } only %} + + {% set list_item1 %} + {% include '@base/icon/icon.twig' with { + symbol: 'lock-file', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'This page must remain in Figma.', + } only %} + {% endset %} + + {% set list_item2 %} + {% include '@base/icon/icon.twig' with { + symbol: 'lock-gallery', + size: 'large', + } only %} + {% include '@atoms/paragraph/paragraph.twig' with { + theme: 'dark', + content: 'The co-branded logo must remain in Storybook and must not be deleted.', + } only %} + {% endset %} + + {% include '@base/item-list/item-list.twig' with { + direction: 'vertical', + items: [ + list_item1, + list_item2, + ], + } only %} +
+
+
+
+
diff --git a/components/00-base/background/background.stories.js b/components/00-base/background/background.stories.js index ea61008a..aa509449 100644 --- a/components/00-base/background/background.stories.js +++ b/components/00-base/background/background.stories.js @@ -1,25 +1,28 @@ -import { color, select } from '@storybook/addon-knobs'; -import { objectFromArray } from '../base.utils'; +import { knobColor, knobSelect, objectFromArray, shouldRender } from '../storybook/storybook.utils'; export default { title: 'Base/Background', parameters: { layout: 'centered', + storyLayoutSize: 'large', + storyLayoutClass: 'story-background-wrapper', }, }; -export const Background = (knobTab) => { - const generalKnobTab = typeof knobTab === 'string' ? knobTab : 'General'; +export const Background = (parentKnobs = {}) => { + const knobs = { + url: knobSelect('Background', Object.keys(BACKGROUNDS), Object.keys(BACKGROUNDS)[0], parentKnobs.bgImageUrl, parentKnobs.knobTab), + color: knobColor('Background color', '#003a4f', parentKnobs.bgColor, parentKnobs.knobTab), + blend_mode: knobSelect( + 'Blend mode', + objectFromArray(SCSS_VARIABLES['ct-background-blend-modes']), + SCSS_VARIABLES['ct-background-blend-modes'][0], + parentKnobs.blendMode, + parentKnobs.knobTab, + ), + }; - const bgImageUrl = select('Background', Object.keys(BACKGROUNDS), Object.keys(BACKGROUNDS)[0], generalKnobTab); - const bgColor = color('Background color', '#003a4f', generalKnobTab); - - const blendMode = select( - 'Blend mode', - objectFromArray(SCSS_VARIABLES['ct-background-blend-modes']), - SCSS_VARIABLES['ct-background-blend-modes'][0], - generalKnobTab, - ); - - return `
`; + return shouldRender(parentKnobs) + ? `
` + : knobs; }; diff --git a/components/00-base/background/background.stories.scss b/components/00-base/background/background.stories.scss index 38da4c4e..df80adcc 100644 --- a/components/00-base/background/background.stories.scss +++ b/components/00-base/background/background.stories.scss @@ -1,12 +1,20 @@ // // Background component stories. // +#root { + > .story-layout { + &.story-background-wrapper { + position: absolute; + width: 80%; + height: 80%; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); -.story-background-wrapper { - position: absolute; - width: 80%; - height: 80%; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + .ct-background { + height: 100%; + width: 100%; + } + } + } } diff --git a/components/00-base/base.stories.scss b/components/00-base/base.stories.scss deleted file mode 100644 index 26606a36..00000000 --- a/components/00-base/base.stories.scss +++ /dev/null @@ -1,199 +0,0 @@ -// -// Base rules for Storybook stories. -// - -@use 'sass:color'; -@use 'sass:map'; - -// Set default styles for the Storybook viewport. -#root { - font-family: sans-serif; - padding: ct-spacing(2); - - // Only apply wrappers used directly in preview, but not within other - // components. - > .story-wrapper-size--small { - max-width: map.get($ct-breakpoints, 'xs'); - } - - > .story-wrapper-size--medium { - max-width: map.get($ct-breakpoints, 's'); - } - - > .story-wrapper-size--large { - max-width: map.get($ct-breakpoints, 'xxl'); - } - - > .story-wrapper--centered { - margin-left: auto; - margin-right: auto; - } - - > .story-wrapper--centered-both { - margin-top: 30vh; - margin-left: auto; - margin-right: auto; - } -} - -.sb-show-main { - overflow-y: scroll; - - &.sb-main-fullscreen, - &.sb-main-centered { - #root { - padding: 0; - } - } -} - -.story-slot { - display: block; - background-color: color.adjust(purple, $alpha: -0.7); - padding: 1em; - text-align: center; - font-family: sans-serif; - border: dotted ct-particle(0.125) purple; - - .ct-theme-dark & { - background-color: color.adjust(white, $alpha: -0.7); - border: dotted ct-particle(0.125) purple; - } -} - -.story-slot--image_over { - position: absolute; - top: 4em; - left: 0; - right: 0; -} - -.story-placeholder { - $_color: grey; - - width: 100%; - display: block; - background-color: color.adjust($_color, $alpha: -0.7); - padding: 1em; - text-align: center; - font-family: sans-serif; - border: dotted ct-particle(0.125) $_color; - color: color.adjust($_color, $alpha: -0.1); - - .ct-theme-dark & { - background-color: color.adjust($_color, $alpha: -0.7); - border: dotted ct-particle(0.125) $_color; - color: color.adjust($_color, $alpha: 0.1); - } -} - -.example-container { - $root: &; - - margin-bottom: ct-spacing(4); - - #{$root}__title { - @include ct-typography('label-large'); - - border-bottom: solid ct-particle(0.125); - margin-bottom: ct-spacing(2); - - code { - text-transform: lowercase; - font-family: 'Courier New', monospace; - background: #eee; - padding: 0 0.25em; - } - } - - #{$root}__subtitle { - @include ct-typography('label-regular'); - - margin-bottom: ct-spacing(1); - } - - #{$root}__content { - margin-bottom: ct-spacing(4); - } - - #{$root}__subcontent { - margin-bottom: ct-spacing(4); - } - - &#{$root}--columns { - display: flex; - } - - &#{$root}--columns--2 { - #{$root}__column { - width: 50%; - padding: ct-spacing(2); - } - } - - #{$root}__column { - flex: 1; - - &--light { - background: ct-color-light('background-light'); - - #{$root}__title, - #{$root}__subtitle { - color: ct-color-light('heading'); - } - - #{$root}__content, - #{$root}__subcontent { - color: ct-color-light('body'); - } - } - - &--dark { - background: ct-color-dark('background'); - - #{$root}__title, - #{$root}__subtitle { - color: ct-color-dark('heading'); - } - - #{$root}__content, - #{$root}__subcontent { - color: ct-color-dark('body'); - } - } - } -} - -.docs-container { - $root: &; - - border: solid ct-particle(0.125) #ccc; - padding: ct-spacing(4); - margin: ct-spacing(4) auto; - max-width: 60%; - - &--small { - max-width: map.get($ct-breakpoints, 'xs'); - } - - &--medium { - max-width: map.get($ct-breakpoints, 's'); - } - - &--large { - margin: ct-spacing(8) auto; - min-width: map.get($ct-breakpoints, 'xxl'); - } - - &--content-centered { - text-align: center; - } - - #{$root}__title { - @include ct-typography('label-large'); - } - - #{$root}__content { - margin-bottom: ct-spacing(4); - } -} diff --git a/components/00-base/base.utils.js b/components/00-base/base.utils.js deleted file mode 100644 index 431eb4a9..00000000 --- a/components/00-base/base.utils.js +++ /dev/null @@ -1,293 +0,0 @@ -// -// Shared JS helpers for Storybook stories. -// - -import { boolean } from '@storybook/addon-knobs'; -import { LoremIpsum } from 'lorem-ipsum'; -import CivicThemeInput from '../01-atoms/input/input.twig'; -import CivicThemeSelect from '../01-atoms/select/select.twig'; -import CivicThemeCheckbox from '../01-atoms/checkbox/checkbox.twig'; -import CivicThemeRadio from '../01-atoms/radio/radio.twig'; -import CivicThemeFormElement - from '../02-molecules/form-element/form-element.twig'; -import CivicThemeLabel from '../01-atoms/label/label.twig'; - -export const getThemes = () => ({ - light: 'Light', - dark: 'Dark', -}); - -export const getSlots = (names) => { - const showSlots = boolean('Show story-slots', false, 'Slots'); - const obj = {}; - - if (showSlots) { - for (const i in names) { - obj[names[i]] = `
{{ ${names[i]} }}
`; - } - } - - return obj; -}; - -export const randomText = (words) => { - const lorem = new LoremIpsum({ - sentencesPerParagraph: { - max: 8, - min: 4, - }, - wordsPerSentence: { - max: 16, - min: 4, - }, - }); - - return lorem.generateWords(words); -}; - -export const placeholder = (content = 'Content placeholder') => `
${content}
`; - -export const capitalizeFirstLetter = (string) => string.charAt(0).toUpperCase() + string.slice(1); - -export const randomInt = (min, max) => { - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(Math.random() * (max - min) + min); -}; - -export const randomBool = (skew) => { - skew = skew || 0.5; - return Math.random() > skew; -}; - -export const randomString = (length) => randomText(length).substring(0, length).trim(); - -export const randomName = (length) => randomText(length).replace(' ', '').substring(0, length).trim(); - -export const randomSentence = (words) => { - words = words || randomInt(5, 25); - return capitalizeFirstLetter(randomText(words)); -}; - -export const randomUrl = (domain) => { - domain = domain || 'http://example.com'; - return `${domain}/${(Math.random() + 1).toString(36).substring(7)}`; -}; - -export const randomLinks = (count, length, domain, prefix) => { - const links = []; - prefix = prefix || 'Link'; - length = length || 0; - - for (let i = 0; i < count; i++) { - links.push({ - text: `${prefix} ${i + 1}${length ? ` ${randomString(randomInt(3, length))}` : ''}`, - url: randomUrl(domain), - is_new_window: randomBool(), - is_external: randomBool(0.8), - }); - } - - return links; -}; - -export const randomTags = (count, rand) => { - const tags = []; - rand = rand || false; - - for (let i = 0; i < count; i++) { - tags.push(`Topic ${i + 1}${rand ? ` ${randomString(randomInt(2, 5))}` : ''}`); - } - - return tags; -}; - -export const demoImage = (idx) => { - const images = [ - './assets/images/demo1.jpg', - './assets/images/demo2.jpg', - './assets/images/demo3.jpg', - './assets/images/demo4.jpg', - './assets/images/demo5.jpg', - './assets/images/demo6.jpg', - ]; - - idx = typeof idx !== 'undefined' ? Math.max(0, Math.min(idx, images.length)) : Math.floor(Math.random() * images.length); - - return images[idx]; -}; - -export const demoVideos = () => [ - { - url: './assets/videos/demo.webm', - type: 'video/webm', - }, - { - url: './assets/videos/demo.mp4', - type: 'video/mp4', - }, - { - url: './assets/videos/demo.avi', - type: 'video/avi', - }, -]; - -export const demoVideoPoster = () => './assets/videos/demo_poster.png'; - -export const demoIcon = () => './assets/icons/megaphone.svg'; - -export const randomFormElement = (inputType, options, theme, rand, itr) => { - const isCheckboxOrRadio = inputType === 'checkbox' || inputType === 'radio'; - - const formElementOptions = { - theme, - type: inputType, - label: CivicThemeLabel({ - theme, - content: options.title ? options.title : `Input title ${itr + 1}${rand ? ` ${randomString(randomInt(2, 5))}` : ''}`, - attributes: `for="form-element-${itr}"`, - required: options.required, - }), - label_display: isCheckboxOrRadio ? 'after' : 'before', - description_position: isCheckboxOrRadio ? 'after' : 'before', - description: { - content: options.description ? `Input description ${itr + 1}${rand ? ` ${randomText(randomInt(4, 10))}` : ''}` : '', - }, - children: [], - attributes: options.form_element_attributes, - }; - let attributes = `id="form-element-${itr}"`; - if (typeof options.attributes !== 'undefined') { - attributes += options.attributes; - } - - const inputOptions = { - theme, - type: inputType, - attributes, - required: options.required, - value: typeof options.value !== 'undefined' ? options.value : randomString(randomInt(3, 8)), - }; - - switch (inputType) { - case 'radio': - formElementOptions.children.push(CivicThemeRadio(inputOptions)); - break; - case 'checkbox': - formElementOptions.children.push(CivicThemeCheckbox(inputOptions)); - break; - case 'select': - formElementOptions.children.push(CivicThemeSelect({ - ...inputOptions, - options: inputOptions.value, - })); - break; - default: - formElementOptions.children.push(CivicThemeInput(inputOptions)); - } - - return CivicThemeFormElement(formElementOptions); -}; - -export const randomFormElements = (count, theme, rand) => { - rand = rand || false; - - const inputTypes = [ - 'text', - 'textarea', - 'tel', - 'password', - 'radio', - 'checkbox', - 'select', - ]; - - const requiredOptions = ['required', '']; - - const formElements = []; - for (let i = 0; i < count; i++) { - const inputType = inputTypes[Math.floor(Math.random() * inputTypes.length)]; - const required = [Math.floor(Math.random() * requiredOptions.length)]; - - formElements.push(randomFormElement( - inputType, - { - required, - }, - theme, - rand, - i, - )); - } - - return formElements; -}; - -export const randomOptions = (numOfOptions, optionType = 'option') => { - const options = []; - for (let i = 1; i <= numOfOptions; i++) { - const option = { - type: optionType, - selected: false, - label: optionType === 'optgroup' ? `Group ${i}` : randomString(randomInt(3, 8)), - value: randomString(randomInt(1, 8)), - options: optionType === 'optgroup' ? randomOptions(numOfOptions) : null, - }; - options.push(option); - } - return options; -}; - -export const generateItems = (count, content) => { - const items = []; - for (let i = 1; i <= count; i++) { - items.push(content); - } - return items; -}; - -export const objectFromArray = (array) => { - const obj = {}; - array.forEach((item) => { - obj[item] = item; - }); - return obj; -}; - -export const cleanCssIdentifier = function (string) { - return string.toLowerCase() - .replace(/(&\w+?;)/gim, ' ') - .replace(/[_.~"<>%|'!*();:@&=+$,/?%#[\]{}\n`^\\]/gim, '') - .replace(/(^\s+)|(\s+$)/gim, '') - .replace(/\s+/gm, '-'); -}; - -export const arrayCombine = function (keys, values) { - const obj = {}; - - if (!keys || !values || keys.constructor !== Array || values.constructor !== Array) { - return false; - } - - if (keys.length !== values.length) { - return false; - } - - for (let i = 0; i < keys.length; i++) { - obj[keys[i]] = values[i]; - } - - return obj; -}; - -export const toLabels = function (values) { - const arr = []; - for (const i in values) { - arr.push(capitalizeFirstLetter(values[i].replace(/[-_]/, ' '))); - } - return arr; -}; - -export const dateIsValid = function (date) { - return !Number.isNaN(Date.parse(date)); -}; diff --git a/components/00-base/collapsible/collapsible.js b/components/00-base/collapsible/collapsible.js index 101d12aa..eec62eb0 100644 --- a/components/00-base/collapsible/collapsible.js +++ b/components/00-base/collapsible/collapsible.js @@ -201,7 +201,7 @@ CivicThemeCollapsible.prototype.keydownEvent = function (e) { if (this !== document) { // Up. if (e.which === 38 && !e.shiftKey) { - this.dispatchEvent(new CustomEvent('ct.collapsible.collapse', { bubbles: true, detail: { animate: true } })); + this.dispatchEvent(new CustomEvent('ct.collapsible.collapse', { bubbles: true, detail: { animate: true, keydown: true } })); return; } @@ -226,7 +226,7 @@ CivicThemeCollapsible.prototype.closeGroup = function (group) { // eslint-disable-next-line prefer-template document.querySelectorAll('[data-collapsible-group=' + group + ']:not([data-collapsible-collapsed])').forEach((el) => { if (el !== currentEl) { - el.dispatchEvent(new CustomEvent('ct.collapsible.collapse', { bubbles: true })); + el.dispatchEvent(new CustomEvent('ct.collapsible.collapse', { bubbles: true, detail: { closeGroup: true } })); } }); } @@ -255,7 +255,11 @@ CivicThemeCollapsible.prototype.collapse = function (animate, evt) { } if (evt && evt.target) { - if (evt.currentTarget !== t.el) { + if (evt.detail && evt.detail.keydown && !evt.detail.closeGroup) { + if (evt.target.closest('[data-collapsible="true"]') !== t.el) { + return; + } + } else if (evt.currentTarget !== t.el || evt.target !== t.el) { return; } } @@ -431,15 +435,19 @@ CivicThemeCollapsible.prototype.htmlToElement = function (html) { return template.content.firstChild; }; -document.querySelectorAll('[data-collapsible]').forEach((el) => { - // Delay initialisation if should be responsive. - const breakpointExpr = el.getAttribute('data-responsive'); - if (breakpointExpr) { - window.addEventListener('ct-responsive', (evt) => { - evt.detail.evaluate(breakpointExpr, CivicThemeCollapsible, el); - }, false); - return; - } +if (typeof document !== 'undefined') { + document.querySelectorAll('[data-collapsible]').forEach((el) => { + // Delay initialisation if should be responsive. + const breakpointExpr = el.getAttribute('data-responsive'); + if (breakpointExpr) { + window.addEventListener('ct-responsive', (evt) => { + evt.detail.evaluate(breakpointExpr, CivicThemeCollapsible, el); + }, false); + return; + } + + new CivicThemeCollapsible(el); + }); +} - new CivicThemeCollapsible(el); -}); +module.exports = CivicThemeCollapsible; diff --git a/components/00-base/collapsible/collapsible.stories.js b/components/00-base/collapsible/collapsible.stories.js index 4b3d2e04..4e643042 100644 --- a/components/00-base/collapsible/collapsible.stories.js +++ b/components/00-base/collapsible/collapsible.stories.js @@ -1,8 +1,12 @@ -import CivicThemeCollapsible from './collapsible.stories.twig'; +import CollapsibleStoryTemplate from './collapsible.stories.twig'; import './collapsible'; export default { - title: 'Base/Collapsible', + title: 'Base/Utilities/Collapsible', + parameters: { + layout: 'centered', + storyLayoutSize: 'medium', + }, }; -export const Collapsible = CivicThemeCollapsible; +export const Collapsible = CollapsibleStoryTemplate; diff --git a/components/00-base/collapsible/collapsible.stories.twig b/components/00-base/collapsible/collapsible.stories.twig index db518315..2f710052 100644 --- a/components/00-base/collapsible/collapsible.stories.twig +++ b/components/00-base/collapsible/collapsible.stories.twig @@ -1,523 +1,521 @@ {# /** * @file - * Collapsible component story. + * Collapsible story template. */ #} -
- + +
+
Initially expanded
+ +
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
- .example-menu-focus button:focus { - outline: solid 2px orange; - } +
+
Initially collapsed
- .example-menu-focus [data-collapsible-trigger]:focus { - outline: solid 2px red; - } - +
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
-
-
Initially expanded
+
+
Long animation
-
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
-
-
Initially collapsed
+
+
No animation
-
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
+ +
+
Minimal data attributes
-
-
Long animation
- -
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
-
-
No animation
- -
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
No trigger icon
+ +
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
-
-
Minimal data attributes
+
+
Wide trigger icon
-
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
+ +
+
Themed
-
-
No trigger icon
- -
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
-
-
Wide trigger icon
- -
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
Trigger is a link - Initially expanded
+ +
+ + TRIGGER (click me) + +
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
-
-
Themed
- -
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
Trigger is a button - Initially expanded
+ +
+ +
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
-
-
Trigger is a link - Initially expanded
- -
- - TRIGGER (click me) - -
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
Inline trigger
+ +
+ + TRIGGER (click me) + +
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
+
-
-
Trigger is a button - Initially expanded
- -
- -
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
+
+
Absolute panel
+ +
+ + TRIGGER (click me) + +
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis.
-
-
Inline trigger
+ Text behind absolutely positioned panel. +
-
- - TRIGGER (click me) - -
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
+
+
+ Multiple inline with absolute panels
-
-
Absolute panel
- -
- - TRIGGER (click me) - -
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
+ + + Text behind absolutely positioned panels. +
- Text behind absolutely positioned panel. +
+
+ Multiple inline with absolute panels and focus
-
-
- Multiple inline with absolute panels -
+
+ +
- - - Text behind absolutely positioned panels. + + + Text behind absolutely positioned panels. + +
+
+
-
-
- Multiple inline with absolute panels and focus -
+
+
Test Nested Collapsible Closed
-
- +
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+ TRIGGER (click me) 1 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
+
+ TRIGGER (click me) 2 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+ TRIGGER (click me) 2:1 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
+
+ TRIGGER (click me) 2:2 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
+
+
+
+ TRIGGER (click me) 3 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
+
- - - Text behind absolutely positioned panels. +
+
Test Nested Collapsible Expanded
-
- +
+
+ TRIGGER (click me) +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+ TRIGGER (click me) 1 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
+
+ TRIGGER (click me) 2 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+ TRIGGER (click me) 2:1 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
+
+ TRIGGER (click me) 2:2 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
+
+
+
+ TRIGGER (click me) 3 +
+
+ Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing + elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! + Accusamus + atque deleniti, distinctio esse facere, nam odio officiis omnis porro + quibusdam quis repudiandae veritatis. +
+
+
-
- -
-
Test Nested Collapsible Closed
- -
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
- TRIGGER (click me) 1 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
-
-
- TRIGGER (click me) 2 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
- TRIGGER (click me) 2:1 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
-
-
- TRIGGER (click me) 2:2 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
-
-
-
-
- TRIGGER (click me) 3 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
-
-
-
- -
-
Test Nested Collapsible Expanded
- -
-
- TRIGGER (click me) -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
- TRIGGER (click me) 1 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
-
-
- TRIGGER (click me) 2 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
- TRIGGER (click me) 2:1 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
-
-
- TRIGGER (click me) 2:2 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
-
-
-
-
- TRIGGER (click me) 3 -
-
- Collapsible demo panel Lorem ipsum dolor sit amet, consectetur adipisicing - elit. Consectetur harum magnam modi obcaecati vitae voluptatibus! - Accusamus - atque deleniti, distinctio esse facere, nam odio officiis omnis porro - quibusdam quis repudiandae veritatis. -
-
-
-
-
diff --git a/components/00-base/colors/colors.stories.js b/components/00-base/colors/colors.stories.js index 3cd7639a..12adf459 100644 --- a/components/00-base/colors/colors.stories.js +++ b/components/00-base/colors/colors.stories.js @@ -1,11 +1,14 @@ -import CivicThemeColors from './colors.stories.twig'; -import { getThemes } from '../base.utils'; +import ColorsStoryTemplate from './colors.stories.twig'; +import { themes } from '../storybook/storybook.generators.utils'; export default { title: 'Base/Colors', + parameters: { + layout: 'fullscreen', + }, }; -function getColourMap(name) { +function getColorMap(name) { const map = {}; map.default = SCSS_VARIABLES[`ct-${name}-default`] || {}; @@ -26,7 +29,7 @@ function getColourMap(name) { }; } - for (const theme in getThemes()) { + for (const theme in themes()) { map.custom[theme] = Object.keys(map.custom[theme]).filter((n) => Object.keys(map.default[theme]).indexOf(n) === -1) .reduce((obj2, key) => { if (key in map.custom[theme]) { @@ -83,8 +86,8 @@ export const Colors = () => { }, }; - const brandMap = getColourMap('colors-brands'); - const paletteMap = getColourMap('colors'); + const brandMap = getColorMap('colors-brands'); + const paletteMap = getColorMap('colors'); const colorMap = { 'Brand colors': brandMap, @@ -93,7 +96,7 @@ export const Colors = () => { const sections = {}; - for (const theme in getThemes()) { + for (const theme in themes()) { for (const sectionTitle in sectionMap) { for (const sectionName in sectionMap[sectionTitle]) { sections[theme] = sections[theme] || {}; @@ -115,7 +118,7 @@ export const Colors = () => { } } - return CivicThemeColors({ + return ColorsStoryTemplate({ sections, }); }; diff --git a/components/00-base/colors/colors.stories.scss b/components/00-base/colors/colors.stories.scss index 9525c624..78567d32 100644 --- a/components/00-base/colors/colors.stories.scss +++ b/components/00-base/colors/colors.stories.scss @@ -32,7 +32,7 @@ .story-color-text { display: block; - @include ct-typography('label-small'); + @include ct-typography('label-extra-small'); text-align: center; margin: ct-spacing() 0; @@ -59,18 +59,18 @@ $colors: _ct-colors(); @include ct-generate-class-variations-from-map(map-get($brand-colors, 'light'), 'story-color-light') using($key, $value, $index) { - @include ct-stories-color-box($key, $value, ct-particle(12), ct-particle(7.5)); + @include ct-stories-color-box($key, $value, ct-particle(12), ct-particle(7.5)); } @include ct-generate-class-variations-from-map(map-get($brand-colors, 'dark'), 'story-color-dark') using($key, $value, $index) { - @include ct-stories-color-box($key, $value, ct-particle(12), ct-particle(7.5)); + @include ct-stories-color-box($key, $value, ct-particle(12), ct-particle(7.5)); } @include ct-generate-class-variations-from-map(map-get($colors, 'light'), 'story-color-light') using($key, $value, $index) { - @include ct-stories-color-box($key, $value, ct-particle(12), ct-particle(7.5)); + @include ct-stories-color-box($key, $value, ct-particle(12), ct-particle(7.5)); } @include ct-generate-class-variations-from-map(map-get($colors, 'dark'), 'story-color-dark') using($key, $value, $index) { - @include ct-stories-color-box($key, $value, ct-particle(12), ct-particle(7.5)); + @include ct-stories-color-box($key, $value, ct-particle(12), ct-particle(7.5)); } } diff --git a/components/00-base/colors/colors.stories.twig b/components/00-base/colors/colors.stories.twig index 86d8a5dc..b3488dee 100644 --- a/components/00-base/colors/colors.stories.twig +++ b/components/00-base/colors/colors.stories.twig @@ -1,24 +1,24 @@ {# /** -* @file -* Colors component story. -*/ + * @file + * Colors story template. + */ #} -
+
{% for theme, theme_section in sections %} -
+
{% for section_title, section_colors in theme_section %} -
{{ section_title }}
+
{{ section_title }}
{% for section_name, colors in section_colors %} -
+
{% if section_name != 'Standard' %} -
{{ section_name }}
+
{{ section_name }}
{% endif %} -
+
{% for color_name, color in colors %}
{{ color_name|capitalize|replace({'-': ' '}) }} diff --git a/components/00-base/datetime/__snapshots__/datetime.test.js.snap b/components/00-base/datetime/__snapshots__/datetime.test.js.snap new file mode 100644 index 00000000..cafb046e --- /dev/null +++ b/components/00-base/datetime/__snapshots__/datetime.test.js.snap @@ -0,0 +1,114 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Datetime Component does not render when start time is empty 1`] = `
`; + +exports[`Datetime Component renders with custom attributes and classes 1`] = ` +
+ + + + + + + + + + +
+`; + +exports[`Datetime Component renders with start and end time 1`] = ` +
+ + + + + + + — + + + + + + + +
+`; + +exports[`Datetime Component renders with start time only 1`] = ` +
+ + + + + + + + + + +
+`; + +exports[`Datetime Component strips HTML tags from attribute values 1`] = ` +
+ + + + + + + — + + + + + + + +
+`; diff --git a/components/00-base/datetime/datetime.stories.js b/components/00-base/datetime/datetime.stories.js new file mode 100644 index 00000000..93c207ae --- /dev/null +++ b/components/00-base/datetime/datetime.stories.js @@ -0,0 +1,23 @@ +import CivicThemeTimestamp from './datetime.twig'; +import { dateIsValid, knobText, shouldRender } from '../storybook/storybook.utils'; + +export default { + title: 'Base/Utilities/Datetime', + parameters: { + layout: 'centered', + }, +}; + +export const Datetime = (parentKnobs = {}) => { + const knobs = { + start: knobText('Start', '20 Jan 2023 11:00', parentKnobs.start, parentKnobs.knobTab), + end: knobText('End', '21 Jan 2023 15:00', parentKnobs.end, parentKnobs.knobTab), + modifier_class: knobText('Additional classes', '', parentKnobs.modifier_class, parentKnobs.knobTab), + attributes: knobText('Additional attributes', '', parentKnobs.attributes, parentKnobs.knobTab), + }; + + knobs.start_iso = dateIsValid(knobs.start) ? new Date(knobs.start).toISOString() : null; + knobs.end_iso = dateIsValid(knobs.end) ? new Date(knobs.end).toISOString() : null; + + return shouldRender(parentKnobs) ? CivicThemeTimestamp(knobs) : knobs; +}; diff --git a/components/00-base/datetime/datetime.test.js b/components/00-base/datetime/datetime.test.js new file mode 100644 index 00000000..08f75439 --- /dev/null +++ b/components/00-base/datetime/datetime.test.js @@ -0,0 +1,73 @@ +const template = 'components/00-base/datetime/datetime.twig'; + +describe('Datetime Component', () => { + test('does not render when start time is empty', async () => { + const c = await dom(template, { + start: '', + }); + + expect(c.querySelectorAll('.ct-datetime')).toHaveLength(0); + }); + + test('renders with start time only', async () => { + const c = await dom(template, { + start: '2023-06-15T08:00', + start_iso: '2023-06-15T08:00:00Z', + }); + + expect(c.querySelectorAll('.ct-datetime')).toHaveLength(1); + expect(c.querySelector('.ct-timestamp__start').getAttribute('datetime')).toEqual('2023-06-15T08:00:00Z'); + expect(c.querySelector('.ct-timestamp__start').textContent.trim()).toEqual('2023-06-15T08:00'); + + assertUniqueCssClasses(c); + }); + + test('renders with start and end time', async () => { + const c = await dom(template, { + start: '2023-06-15T08:00', + start_iso: '2023-06-15T08:00:00Z', + end: '2023-06-15T17:00', + end_iso: '2023-06-15T17:00:00Z', + }); + + expect(c.querySelectorAll('.ct-datetime')).toHaveLength(1); + expect(c.querySelector('.ct-timestamp__start').getAttribute('datetime')).toEqual('2023-06-15T08:00:00Z'); + expect(c.querySelector('.ct-timestamp__start').textContent.trim()).toEqual('2023-06-15T08:00'); + expect(c.querySelector('.ct-timestamp__end').getAttribute('datetime')).toEqual('2023-06-15T17:00:00Z'); + expect(c.querySelector('.ct-timestamp__end').textContent.trim()).toEqual('2023-06-15T17:00'); + + assertUniqueCssClasses(c); + }); + + test('renders with custom attributes and classes', async () => { + const c = await dom(template, { + start: '2023-06-15T08:00', + start_iso: '2023-06-15T08:00:00Z', + modifier_class: 'custom-class', + attributes: 'data-test="true"', + }); + + expect(c.querySelectorAll('.ct-datetime.custom-class')).toHaveLength(1); + expect(c.querySelector('.ct-datetime').getAttribute('data-test')).toEqual('true'); + expect(c.querySelector('.ct-timestamp__start').getAttribute('datetime')).toEqual('2023-06-15T08:00:00Z'); + expect(c.querySelector('.ct-timestamp__start').textContent.trim()).toEqual('2023-06-15T08:00'); + + assertUniqueCssClasses(c); + }); + + test('strips HTML tags from attribute values', async () => { + const c = await dom(template, { + start: '2023-06-15T08:00', + start_iso: '2023-06-15T08:00:00Z', + end: '2023-06-15T17:00', + end_iso: '2023-06-15T17:00:00Z', + }); + + expect(c.querySelector('.ct-timestamp__start').getAttribute('datetime')).toEqual('2023-06-15T08:00:00Z'); + expect(c.querySelector('.ct-timestamp__start').innerHTML.trim()).toEqual('2023-06-15T08:00'); + expect(c.querySelector('.ct-timestamp__end').getAttribute('datetime')).toEqual('2023-06-15T17:00:00Z'); + expect(c.querySelector('.ct-timestamp__end').innerHTML.trim()).toEqual('2023-06-15T17:00'); + + assertUniqueCssClasses(c); + }); +}); diff --git a/components/00-base/time/time.twig b/components/00-base/datetime/datetime.twig similarity index 53% rename from components/00-base/time/time.twig rename to components/00-base/datetime/datetime.twig index bb708bcb..4e027162 100644 --- a/components/00-base/time/time.twig +++ b/components/00-base/datetime/datetime.twig @@ -1,7 +1,7 @@ {# /** * @file - * Time component. + * Datetime component. * * Variables: * - start: [string] Formatted start time. @@ -14,11 +14,11 @@ #} {% if start is not empty %} - - + + {% if end is not empty %} — - + {% endif %} {% endif %} diff --git a/components/00-base/elevation/elevation.stories.js b/components/00-base/elevation/elevation.stories.js index 6a671883..af89f922 100644 --- a/components/00-base/elevation/elevation.stories.js +++ b/components/00-base/elevation/elevation.stories.js @@ -8,9 +8,9 @@ export const Elevation = () => { html += `
`; for (let i = 1; i <= 4; i++) { - html += `
`; - html += `
Elevation ${i}
`; - html += `
`; + html += `
`; + html += `
Elevation ${i}
`; + html += `
`; html += `
`; } diff --git a/components/00-base/elevation/elevation.stories.scss b/components/00-base/elevation/elevation.stories.scss index 8df66231..fa8942c4 100644 --- a/components/00-base/elevation/elevation.stories.scss +++ b/components/00-base/elevation/elevation.stories.scss @@ -11,7 +11,7 @@ @include ct-generate-class-variations-from-map($ct-elevations, 'story-elevation') using($key, $value, $index) { width: ct-particle(20); height: ct-particle(20); - margin: ct-spacing(6) ct-spacing(10) ct-particle(12) 0; + margin: ct-spacing(6) ct-spacing(10) ct-particle(12) 0; border-radius: ct-spacing(0.5); position: relative; background-color: white; diff --git a/components/00-base/flex/_index.scss b/components/00-base/flex/_index.scss new file mode 100644 index 00000000..64e804c5 --- /dev/null +++ b/components/00-base/flex/_index.scss @@ -0,0 +1,37 @@ +// +// Flex utilities. +// + +.ct-flex-justify-content-start { + display: flex; + justify-content: flex-start; +} + +.ct-flex-justify-content-center { + display: flex; + justify-content: center; +} + +.ct-flex-justify-content-end { + display: flex; + justify-content: flex-end; +} + +.ct-flex-row-align-middle { + display: flex; + flex-direction: row; + align-items: center; + height: 100%; +} + +.ct-flex-align-self-start { + align-self: flex-start; +} + +.ct-flex-align-self-center { + align-self: center; +} + +.ct-flex-align-self-end { + align-self: flex-end; +} diff --git a/components/00-base/flyout/flyout.js b/components/00-base/flyout/flyout.js index ac27f206..c6d3d0cc 100644 --- a/components/00-base/flyout/flyout.js +++ b/components/00-base/flyout/flyout.js @@ -59,6 +59,24 @@ function CivicThemeFlyout(el) { }); } + document.addEventListener('keydown', (event) => { + if (event.key === 'Tab') { + const flyoutElements = document.querySelectorAll('[data-flyout]'); + flyoutElements.forEach((flyout) => { + const focusableElements = flyout.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'); + const firstFocusable = focusableElements[0]; + const lastFocusable = focusableElements[focusableElements.length - 1]; + if (document.activeElement === lastFocusable && !event.shiftKey) { + event.preventDefault(); + firstFocusable.focus(); + } else if (document.activeElement === firstFocusable && event.shiftKey) { + event.preventDefault(); + lastFocusable.focus(); + } + }); + } + }); + // Mark as initialized. this.el.setAttribute('data-flyout', 'true'); } @@ -183,7 +201,11 @@ CivicThemeFlyout.prototype.collapse = function () { }; // Initialize CivicThemeFlyout on every element. -document.querySelectorAll('[data-flyout]').forEach((flyout) => { - // eslint-disable-next-line no-new - new CivicThemeFlyout(flyout); -}); +if (typeof document !== 'undefined') { + document.querySelectorAll('[data-flyout]').forEach((flyout) => { + // eslint-disable-next-line no-new + new CivicThemeFlyout(flyout); + }); +} + +module.exports = CivicThemeFlyout; diff --git a/components/00-base/flyout/flyout.stories.js b/components/00-base/flyout/flyout.stories.js index 44a7256e..c20012e1 100644 --- a/components/00-base/flyout/flyout.stories.js +++ b/components/00-base/flyout/flyout.stories.js @@ -1,18 +1,17 @@ -import { boolean, number, radios } from '@storybook/addon-knobs'; - import './flyout'; -import CivicThemeFlyout from './flyout.stories.twig'; +import FlyoutStoryTemplate from './flyout.stories.twig'; +import { knobBoolean, knobNumber, knobRadios, shouldRender } from '../storybook/storybook.utils'; export default { - title: 'Base/Flyout', + title: 'Base/Utilities/Flyout', parameters: { layout: 'centered', }, }; -export const Flyout = () => { - const html = CivicThemeFlyout({ - direction: radios( +export const Flyout = (parentKnobs = {}) => { + const knobs = { + direction: knobRadios( 'Flyout from', { Top: 'top', @@ -21,10 +20,12 @@ export const Flyout = () => { Right: 'right', }, 'right', + parentKnobs.direction, + parentKnobs.knobTab, ), - expanded: boolean('Expanded', false), - duration: number('Duration (ms)', 500), - }); + expanded: knobBoolean('Expanded', false, parentKnobs.expanded, parentKnobs.knobTab), + duration: knobNumber('Duration (ms)', 500, undefined, parentKnobs.duration, parentKnobs.knobTab), + }; - return `
${html}
`; + return shouldRender(parentKnobs) ? FlyoutStoryTemplate(knobs) : knobs; }; diff --git a/components/00-base/flyout/flyout.stories.scss b/components/00-base/flyout/flyout.stories.scss index 6ce945a3..d53d11f2 100644 --- a/components/00-base/flyout/flyout.stories.scss +++ b/components/00-base/flyout/flyout.stories.scss @@ -2,35 +2,35 @@ // Flyout component. // -#flyout-example1 { +#flyout-story1 { [data-flyout-panel] { box-shadow: 0 0 ct-particle(0.125) ct-particle(0.125) #f00 inset; padding: ct-spacing(2); } } -#flyout-example2 { +#flyout-story2 { [data-flyout-panel] { box-shadow: 0 0 ct-particle(0.125) ct-particle(0.125) #00f inset; padding: ct-spacing(2); } } -#flyout-example3 { +#flyout-story3 { [data-flyout-panel] { box-shadow: 0 0 ct-particle(0.125) ct-particle(0.125) #0f0 inset; padding: ct-spacing(2); } } -#flyout-example4 { +#flyout-story4 { [data-flyout-panel] { box-shadow: 0 0 ct-particle(0.125) ct-particle(0.125) #c95100 inset; padding: ct-spacing(2); } } -#flyout-example5 { +#flyout-story5 { [data-flyout-panel] { box-shadow: 0 0 ct-particle(0.125) ct-particle(0.125) #b388ff inset; padding: ct-spacing(2); diff --git a/components/00-base/flyout/flyout.stories.twig b/components/00-base/flyout/flyout.stories.twig index 093efd4c..9f22242d 100644 --- a/components/00-base/flyout/flyout.stories.twig +++ b/components/00-base/flyout/flyout.stories.twig @@ -1,17 +1,17 @@ {# /** * @file - * Flyout component stories. + * Flyout story template. */ #} - + -
+
- Close + Close x
Flyout 1
@@ -25,13 +25,13 @@
- + -
+
- Close + Close ❌
Flyout 2
@@ -46,11 +46,11 @@
-
+
- Close + Close ❌
Flyout 3
@@ -65,11 +65,11 @@
-
+
- Close + Close ❌
Flyout 4
@@ -83,12 +83,12 @@
- -
+ +
- Close all + Close all ❌
Flyout 5
diff --git a/components/00-base/fonts/fonts.stories.js b/components/00-base/fonts/fonts.stories.js index 5e7b1600..1f33ca13 100644 --- a/components/00-base/fonts/fonts.stories.js +++ b/components/00-base/fonts/fonts.stories.js @@ -1,10 +1,13 @@ import merge from 'deepmerge'; -import { capitalizeFirstLetter, cleanCssIdentifier } from '../base.utils'; +import { capitalizeFirstLetter, cleanCssIdentifier } from '../storybook/storybook.utils'; export default { title: 'Base/Fonts', parameters: { layout: 'centered', + storyLayoutSize: 'large', + storyLayoutCenteredHorizontally: true, + storyLayoutClass: 'story-fonts-wrapper story-wrapper-size--large', }, }; @@ -15,22 +18,22 @@ export const Fonts = () => { let html = ''; for (const i in Object.values(fonts)) { - html += `
`; + html += `
`; - html += `
${fonts[i]}
`; + html += `
${capitalizeFirstLetter(fonts[i])}
`; - html += `
`; + html += `
`; for (const weightName in weights) { - html += `
${capitalizeFirstLetter(weightName)}
`; - html += `
The quick brown fox jumps over the lazy dog
`; + html += `
${capitalizeFirstLetter(weightName)}
`; + html += `
The quick brown fox jumps over the lazy dog
`; - html += `
${capitalizeFirstLetter(weightName)} Italic
`; - html += `
The quick brown fox jumps over the lazy dog
`; + html += `
${capitalizeFirstLetter(weightName)} Italic
`; + html += `
The quick brown fox jumps over the lazy dog
`; } html += `
`; html += `
`; } - return `
${html}
`; + return html; }; diff --git a/components/00-base/grid/__snapshots__/grid.test.js.snap b/components/00-base/grid/__snapshots__/grid.test.js.snap new file mode 100644 index 00000000..308bec2f --- /dev/null +++ b/components/00-base/grid/__snapshots__/grid.test.js.snap @@ -0,0 +1,1657 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Grid Attributes and Modifier Class renders { + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + use_container: false +} 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + +
+`; + +exports[`Grid Attributes and Modifier Class renders { + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + use_container: true +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Container renders { use_container: false, is_fluid: false } 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + +
+`; + +exports[`Grid Container renders { use_container: false, is_fluid: true } 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + +
+`; + +exports[`Grid Container renders { use_container: true, is_fluid: false } 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Container renders { use_container: true, is_fluid: true } 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 0, + row_class: 'custom-row', + column_class: 'custom-col', + fill_width: false, + expectedRowClass: 'row custom-row', + expectedColumnClass: 'col custom-col' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 0, + row_class: 'custom-row', + column_class: 'custom-col', + fill_width: true, + expectedRowClass: 'row row--fill-width custom-row', + expectedColumnClass: 'col custom-col' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 0, + row_class: null, + column_class: null, + fill_width: false, + expectedRowClass: 'row', + expectedColumnClass: 'col' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 0, + row_class: null, + column_class: null, + fill_width: true, + expectedRowClass: 'row row--fill-width', + expectedColumnClass: 'col' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 12, + row_class: 'custom-row', + column_class: 'custom-col', + fill_width: false, + expectedRowClass: 'row custom-row', + expectedColumnClass: 'col-xxs-12 col-m-1 custom-col' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 12, + row_class: 'custom-row', + column_class: 'custom-col', + fill_width: true, + expectedRowClass: 'row row--fill-width custom-row', + expectedColumnClass: 'col-xxs-12 col-m-1 custom-col' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 12, + row_class: 'custom-row', + column_class: null, + fill_width: false, + expectedRowClass: 'row custom-row', + expectedColumnClass: 'col-xxs-12 col-m-1' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 12, + row_class: 'custom-row', + column_class: null, + fill_width: true, + expectedRowClass: 'row row--fill-width custom-row', + expectedColumnClass: 'col-xxs-12 col-m-1' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 12, + row_class: null, + column_class: null, + fill_width: false, + expectedRowClass: 'row', + expectedColumnClass: 'col-xxs-12 col-m-1' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: 12, + row_class: null, + column_class: null, + fill_width: true, + expectedRowClass: 'row row--fill-width', + expectedColumnClass: 'col-xxs-12 col-m-1' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: null, + row_class: null, + column_class: null, + fill_width: false, + expectedRowClass: 'row', + expectedColumnClass: 'col' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Classes renders { + template_column_count: null, + row_class: null, + column_class: null, + fill_width: true, + expectedRowClass: 'row row--fill-width', + expectedColumnClass: 'col' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Elements renders { row_element: 'div', column_element: 'div' } 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Custom Elements renders { row_element: 'section', column_element: 'span' } 1`] = ` +
+ + + + + + + +
+ + +
+ + + + + 1 + + + + +
+ + +
+ + +
+`; + +exports[`Grid Row Fill Width renders { fill_width: false } 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid Row Fill Width renders { fill_width: true } 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: false, + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + description: 'attributes and modifier class without container' +} 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: false, + description: 'multiple items without container' +} 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + 2 + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: false, + description: 'without container' +} 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: false, + fill_width: true, + description: 'fill width without container' +} 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + 2 + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: false, + row_class: 'custom-row', + column_class: 'custom-col', + description: 'custom classes without container' +} 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: false, + row_element: 'section', + column_element: 'span', + description: 'custom elements without container' +} 1`] = ` +
+ + + + + + + + +
+ + + + + 1 + + + + + + + 2 + + + + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: false, + template_column_count: 3, + description: 'column count without container' +} 1`] = ` +
+ + + + + + + + +
+ + +
+ + 1 + +
+ + +
+ + 2 + +
+ + +
+ + 3 + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: true, + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + description: 'attributes and modifier class with container' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: true, + description: 'multiple items with container' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + 2 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: true, + fill_width: true, + description: 'fill width with container' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + 2 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: true, + is_fluid: true, + description: 'multiple items with fluid container' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + 2 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: true, + row_class: 'custom-row', + column_class: 'custom-col', + description: 'custom classes with container' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: true, + row_element: 'section', + column_element: 'span', + description: 'custom elements with container' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + + + + 1 + + + + + + + 2 + + + + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: true, + template_column_count: 3, + description: 'column count with container' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + 2 + +
+ + +
+ + 3 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { + items: [Array], + use_container: true, + template_column_count: 3, + is_fluid: true, + description: 'column count with fluid container' +} 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + 2 + +
+ + +
+ + 3 + +
+ + +
+ + +
+ + +
+`; + +exports[`Grid renders correctly { items: [Array], use_container: true, description: 'with container' } 1`] = ` +
+ + + + + + + +
+ + +
+ + +
+ + 1 + +
+ + +
+ + +
+ + +
+`; diff --git a/components/00-base/grid/_index.scss b/components/00-base/grid/_index.scss deleted file mode 100644 index b4c07c0c..00000000 --- a/components/00-base/grid/_index.scss +++ /dev/null @@ -1,154 +0,0 @@ -// -// Grid component. -// - -@use 'sass:map'; - -.row { - @include ct-row($ct-grid-gutters); - - &--no-wrap { - flex-wrap: nowrap; - } - - &.reverse { - @include ct-row-reverse(); - } - - &.natural-height { - @include ct-row-natural-height(); - } - - &.flex-column { - @include ct-row-flex-column(); - } -} - -.col { - // Make columns at the lowest breakpoint to act as a single column per row. - @include _ct-grid-col($ct-grid-lowest-breakpoint, $ct-grid-columns); - - $next-bp: ct-map-get-next($ct-breakpoints, $ct-grid-lowest-breakpoint, map.get($ct-breakpoints, 'xxs'), key); - - // Spawn columns to fit into as single row for larger breakpoints. - @include ct-breakpoint($next-bp) { - @include _ct-grid-col(); - } - - &.reverse { - @include ct-col-reverse(); - } - - &--no-grow { - flex-grow: initial; - } -} - -.first { - order: -1; -} - -.last { - order: 1; -} - -.align-start { - align-self: flex-start; -} - -.align-end { - align-self: flex-end; -} - -.align-center { - align-self: center; -} - -.align-baseline { - align-self: baseline; -} - -.align-stretch { - align-self: stretch; -} - -// Generate column classes. -@each $breakpoint, $value in $ct-breakpoints { - @if $breakpoint == $ct-grid-lowest-breakpoint { - @include _ct-col-factory($breakpoint); - } - @else { - @include ct-breakpoint($breakpoint) { - @include _ct-col-factory($breakpoint); - } - } -} - -// Container class should wrap every row. -.container { - @include ct-container(); -} - -// -// Max width container class to limit the width of the fluid containers at max width. -// -.container-max-width { - @include ct-breakpoint(xxl) { - width: $ct-grid-max-width; - margin: 0 auto; - } -} - -// -// Offset container class to add offset on small screens. -// -.container-offset-xs { - @include ct-breakpoint-upto(m) { - padding-left: map.get($ct-grid-offsets, 'xxs'); - padding-right: map.get($ct-grid-offsets, 'xxs'); - width: auto; - } -} - -.row-no-gap { - margin-left: 0; - margin-right: 0; -} - -.col-no-gap { - padding: 0; -} - -.ct-justify-content-start { - display: flex; - justify-content: flex-start; -} - -.ct-justify-content-center { - display: flex; - justify-content: center; -} - -.ct-justify-content-end { - display: flex; - justify-content: flex-end; -} - -.ct-text-align-left { - text-align: left; -} - -.ct-text-align-center { - text-align: center; -} - -.ct-text-align-right { - text-align: right; -} - -.ct-align-middle { - display: flex; - flex-direction: row; - align-items: center; - height: 100%; -} diff --git a/components/00-base/grid/grid.scss b/components/00-base/grid/grid.scss new file mode 100644 index 00000000..52c023f5 --- /dev/null +++ b/components/00-base/grid/grid.scss @@ -0,0 +1,162 @@ +// +// Grid component. +// + +@use 'sass:map'; + +// Container class should wrap every row. +.container { + @include ct-container(); +} + +.container-fluid { + @include ct-container(false); +} + +.row { + $root: &; + + @include ct-row($ct-grid-gutters); + + &#{$root}--no-gutters { + margin-right: 0; + margin-left: 0; + + > .col, + > [class*='col-'] { + padding-right: 0; + padding-left: 0; + } + } + + &#{$root}--reverse { + flex-direction: row-reverse; + } + + &#{$root}--no-grow { + > .col, + > [class*='col-'] { + flex-grow: initial; + flex-basis: auto; + } + } + + &#{$root}--fill-width { + > .col, + > [class*='col-'] { + max-width: 100%; + flex-grow: 1; + } + } + + &#{$root}--equal-heights-content { + > .col, + > [class*='col-'] { + > * { + height: 100%; + } + } + } + + &#{$root}--unequal-heights { + > .col, + > [class*='col-'] { + margin-bottom: auto; + } + } + + &#{$root}--vertically-spaced { + @each $bp, $gutter in $ct-grid-vertical-gutters { + @if $bp == $ct-grid-lowest-breakpoint { + row-gap: $gutter; + margin-bottom: $gutter; + } + @else { + @include ct-breakpoint($bp) { + row-gap: $gutter; + margin-bottom: $gutter; + } + } + } + } +} + +ul.row { + margin-top: 0; + margin-bottom: 0; +} + +.col { + $root: &; + + // Make columns at the lowest breakpoint to act as a single column per row. + @include _ct-grid-col($ct-grid-lowest-breakpoint, $ct-grid-columns); + + $next-bp: ct-map-get-next($ct-breakpoints, $ct-grid-lowest-breakpoint, map.get($ct-breakpoints, 'xxs'), key); + + // Spawn columns to fit into as single row for larger breakpoints. + @include ct-breakpoint($next-bp) { + @include _ct-grid-col(); + } +} + +// Generate column classes. +@each $breakpoint, $value in $ct-breakpoints { + @if $breakpoint == $ct-grid-lowest-breakpoint { + @include _ct-col-factory($breakpoint); + } + @else { + @include ct-breakpoint($breakpoint) { + @include _ct-col-factory($breakpoint); + } + } +} + +// Utilities for fixed and auto columns. +.col, +[class*='col-'] { + &.col--reverse { + display: flex; + flex-direction: column-reverse; + } + + &.col--no-grow { + flex-grow: initial; + flex-basis: auto; + } + + &.col--no-gap { + // Remove the offsets starting from the next breakpoint from the lowest one. + @include ct-breakpoint($ct-grid-responsive-breakpoint) { + &:not(:first-child) { + padding-left: 0; + } + + &:not(:last-child) { + padding-right: 0; + } + } + } +} + +@each $breakpoint, $value in $ct-breakpoints { + @if $breakpoint == $ct-grid-lowest-breakpoint { + .first { + order: -1; + } + + .last { + order: 1; + } + } + @else { + @include ct-breakpoint($breakpoint) { + .first-#{$breakpoint} { + order: -1; + } + .last-#{$breakpoint} { + order: 1; + } + } + } +} diff --git a/components/00-base/grid/grid.stories.js b/components/00-base/grid/grid.stories.js index 1d1eed6f..d0a1191d 100644 --- a/components/00-base/grid/grid.stories.js +++ b/components/00-base/grid/grid.stories.js @@ -1,118 +1,558 @@ +import CivicThemeGrid from './grid.twig'; +import { code, generateItems, knobBoolean, knobNumber, knobRadios, knobText, placeholder, randomSentence, shouldRender } from '../storybook/storybook.utils'; + export default { title: 'Base/Grid', parameters: { layout: 'fullscreen', + docs: '
Outline colors:
Contained containerFluid containerRowTemplate columnAuto columnPlaceholder
', + docsClass: 'story-docs--conditional', }, }; -export const Grid = () => ` -
-
Columns
-
-
-
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
-
-
2
-
2
-
2
-
2
-
2
-
2
-
-
-
3
-
3
-
3
-
3
-
-
-
4
-
4
-
4
-
-
-
6
-
6
-
-
-
-
- -
-
Responsive
-
-
-
-
Column
-
Column
-
-
-
Column
-
Column
-
Column
-
-
-
-
- -
-
Grid in a grid
-
-
-
-
-
-
Column in nested grid
-
Column in nested grid
-
Column in nested grid
-
-
-
- Column in parent grid -
-
-
-
-
- -
-
Auto Column
-
-
-
-
A
-
B
-
C
-
D
-
-
-
-
- -
-
Reverse Row
-
-
-
-
A
-
B
-
C
-
D
-
-
-
-
-
`; +export const Grid = () => { + const showOutline = knobBoolean('Show outlines', false); + + let cols = []; + + let html = `
`; + + html += `
Container
`; + + html += `
Contained container ${code('.container')}
`; + cols = [12]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(cols[j], placeholder(code(Math.floor(12 / cols[j])))), + column_attributes: `data-story-total-columns="${cols[j]}"`, + template_column_count: cols[j], + }); + } + + html += `
Fluid container ${code('.container-fluid')}
`; + cols = [12]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(cols[j], placeholder(code(Math.floor(12 / cols[j])))), + column_attributes: `data-story-total-columns="${cols[j]}"`, + template_column_count: cols[j], + is_fluid: true, + }); + } + + html += `
Columns
`; + + html += `
Template column in container ${code('.container > .row > .col-[breakpoint]-[column]')}
`; + cols = [1, 2, 3, 4, 6, 12]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(cols[j], placeholder(code(Math.floor(12 / cols[j])))), + column_attributes: `data-story-total-columns="${cols[j]}"`, + template_column_count: cols[j], + }); + } + + html += `
Template column in fluid container ${code('.container-fluid > .row > .col-[breakpoint]-[column]')}
`; + cols = [1]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(cols[j], placeholder(code(Math.floor(12 / cols[j])))), + column_attributes: `data-story-total-columns="${cols[j]}"`, + template_column_count: cols[j], + is_fluid: true, + }); + } + + html += `
Auto column in container ${code('.container .row > .col')}
`; + cols = [1, 2, 3, 4, 6, 12]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(cols[j], placeholder(code('auto'))), + column_attributes: `data-story-total-columns="${cols[j]}"`, + }); + } + + html += `
Offsets and order
`; + + html += `
Template column in container ${code('.container > .row > .col-[breakpoint]-offset-[column]')}
`; + cols = [1, 2, 3, 4, 6, 12]; + let offsets = [1, 2, 3, 4, 6, 8]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(1, placeholder(`width ${code(Math.floor(12 / cols[j]))}, offset ${code(offsets[j])}`)), + column_attributes: `data-story-total-columns="1"`, + column_class: `col-m-offset-${offsets[j]}`, + template_column_count: cols[j], + }); + } + + html += `
Auto column in container ${code('.container > .row > .col.col-[breakpoint]-offset-[column]')}
`; + cols = [1, 2, 3, 4, 6, 12]; + offsets = [1, 2, 3, 4, 6, 8]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(1, placeholder(`width ${code('auto')}, offset ${code(offsets[j])}`)), + column_attributes: `data-story-total-columns="1"`, + column_class: `col-m-offset-${offsets[j]}`, + }); + } + + html += `
Content width
`; + + html += `
Filled ${code('width: 100%')}
`; + cols = ['A', 'B', 'C', 'D', 'E']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + column_class: 'col', + }); + + html += `
Hugged ${code('width: auto')}
`; + cols = ['A', 'B', 'C', 'D', 'E']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1], 0, 'story-placeholder--hugged')), + column_attributes: `data-story-total-columns="${cols.length}"`, + column_class: 'col', + }); + + html += `
Fixed ${code('width: 184px')}
`; + cols = ['A', 'B', 'C', 'D', 'E']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(`${cols[i - 1]} fixed width`, 0, 'story-placeholder--fixed')), + column_attributes: `data-story-total-columns="${cols.length}"`, + column_class: 'col', + }); + + html += `
Nested Rows (container-less)
`; + + html += `
Template column wraps template column ${code('.row > .col-[breakpoint]-[column] > .row > .col-[breakpoint]-[column]')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: false, + template_column_count: 3, + column_attributes: 'data-story-total-columns="3"', + }), + placeholder('Parent'), + ], + use_container: false, + template_column_count: 2, + column_attributes: 'data-story-total-columns="2"', + modifier_class: 'row--no-gutters', + }); + + html += `
Template column wraps auto column ${code('.row > .col-[breakpoint]-[column] > .row > .col')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: false, + column_attributes: 'data-story-total-columns="3"', + }), + placeholder('Parent'), + ], + use_container: false, + template_column_count: 2, + column_attributes: 'data-story-total-columns="2"', + modifier_class: 'row--no-gutters', + }); + + html += `
Auto column wraps auto-column ${code('.row > .col > .row > .col')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: false, + }), + placeholder('Parent'), + ], + use_container: false, + modifier_class: 'row--no-gutters', + }); + + html += `
Auto column wraps template column ${code('.row > .col > .row > .col-[breakpoint]-[column]')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: false, + template_column_count: 3, + column_attributes: 'data-story-total-columns="3"', + }), + placeholder('Parent'), + ], + use_container: false, + modifier_class: 'row--no-gutters', + }); + + html += `
Nested Containers
`; + + html += `
Container with template columns wraps container with template columns ${code('.container .row > .col-[breakpoint]-[column] .container > .row > .col-[breakpoint]-[column]')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: true, + is_fluid: false, + column_attributes: 'data-story-total-columns="3"', + }), + placeholder('Parent'), + ], + use_container: true, + is_fluid: false, + template_column_count: 2, + column_attributes: 'data-story-total-columns="2"', + }); + + html += `
Fluid container with template columns wraps container with template columns ${code('.container-fluid .row > .col-[breakpoint]-[column] .container > .row > .col-[breakpoint]-[column]')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: true, + is_fluid: false, + column_attributes: 'data-story-total-columns="3"', + }), + placeholder('Parent'), + ], + use_container: true, + is_fluid: true, + template_column_count: 2, + column_attributes: 'data-story-total-columns="2"', + }); + + html += `
Fluid container with template columns wraps container with template columns (single column) ${code('.container-fluid .row > .col-[breakpoint]-[column] .container > .row > .col-[breakpoint]-[column]')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: true, + is_fluid: false, + column_attributes: 'data-story-total-columns="3"', + }), + ], + use_container: true, + is_fluid: true, + template_column_count: 1, + }); + + html += `
Container with template columns wraps fluid container with template columns ${code('.container .row > .col-[breakpoint]-[column] .container-fluid > .row > .col-[breakpoint]-[column]')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: true, + is_fluid: true, + column_attributes: 'data-story-total-columns="3"', + }), + placeholder('Parent'), + ], + use_container: true, + is_fluid: false, + template_column_count: 2, + column_attributes: 'data-story-total-columns="2"', + }); + + html += `
Fluid container with template columns wraps fluid container with template columns ${code('.container-fluid .row > .col-[breakpoint]-[column] .container-fluid > .row > .col-[breakpoint]-[column]')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: [ + CivicThemeGrid({ + items: [ + placeholder('Nested'), + placeholder('Nested'), + placeholder('Nested'), + ], + use_container: true, + is_fluid: true, + column_attributes: 'data-story-total-columns="3"', + }), + placeholder('Parent'), + ], + use_container: true, + is_fluid: true, + template_column_count: 2, + column_attributes: 'data-story-total-columns="2"', + }); + + html += `
Row utilities
`; + + html += `
No gutters ${code('.row.row--no-gutters')}
`; + cols = ['A', 'B', 'C', 'D', 'E']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + use_container: false, + row_class: 'row row--no-gutters', + }); + + html += `
No gutters within container ${code('.container > .row.row--no-gutters')}
`; + cols = ['A', 'B', 'C', 'D', 'E']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + use_container: true, + is_fluid: false, + row_class: 'row row--no-gutters', + }); + + html += `
No gutters within fluid container ${code('.container-fluid > .row.row--no-gutters')}
`; + cols = ['A', 'B', 'C', 'D', 'E']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + use_container: true, + is_fluid: true, + row_class: 'row row--no-gutters', + }); + + html += `
Reversed columns ${code('.row.row--reverse')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + template_column_count: cols.length, + row_class: 'row row--reverse', + }); + + html += `
Equal column heights by default
`; + cols = [`Content should not fill - height is not 100%. ${randomSentence(5, 'A')}`, randomSentence(30, 'B'), randomSentence(5, 'C'), randomSentence(5, 'D')]; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + template_column_count: cols.length, + }); + + html += `
Equal column heights propagated to content ${code('.row.row--equal-heights-content > .col-[breakpoint]-[column]')}
`; + cols = [`Content should fill - height is propagated to be 100%. ${randomSentence(5, 'A')}`, randomSentence(30, 'B'), randomSentence(5, 'C'), randomSentence(5, 'D')]; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + template_column_count: cols.length, + row_class: 'row row--equal-heights-content', + }); + + html += `
Equal column heights propagated to content ${code('.row.row--equal-heights-content > .col')} - Auto column
`; + cols = [`Content should fill - height is propagated to be 100%. ${randomSentence(5, 'A')}`, randomSentence(30, 'B'), randomSentence(5, 'C'), randomSentence(5, 'D')]; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + row_class: 'row row--equal-heights-content', + }); + + html += `
Unequal column heights ${code('.row.row--unequal-heights > .col-[breakpoint]-[column]')}
`; + cols = [randomSentence(5, 'A'), randomSentence(20, 'B'), randomSentence(5, 'C'), randomSentence(5, 'D')]; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + template_column_count: cols.length, + row_class: 'row row--unequal-heights', + }); + + html += `
Unequal column heights ${code('.row.row--unequal-heights > .col')} - Auto column
`; + cols = [randomSentence(5, 'A'), randomSentence(20, 'B'), randomSentence(5, 'C'), randomSentence(5, 'D')]; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + row_class: 'row row--unequal-heights', + }); + + html += `
Vertical spacing ${code('.row.row--vertically-spaced > .col-[breakpoint]-[column]')}
`; + cols = [4, 3, 4]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(cols[j] + (j % 2), placeholder(code(Math.floor(12 / cols[j])))), + column_attributes: `data-story-total-columns="${cols[j]}"`, + template_column_count: cols[j], + row_class: 'row row--vertically-spaced', + }); + } + + html += `
Vertical spacing ${code('.row.row--vertically-spaced > .col')} - Autocolumn
`; + cols = [4, 3, 4]; + for (let j = 0; j < cols.length; j++) { + html += CivicThemeGrid({ + items: generateItems(cols[j] + (j % 2), placeholder(code(Math.floor(12 / cols[j])), 0, 'story-placeholder--fixed')), + column_attributes: `data-story-total-columns="${cols[j]}"`, + row_class: 'row row--vertically-spaced', + }); + } + + html += `
Column utilities
`; + + html += `
Reversed items ${code('.row > .col-[breakpoint]-[column].col--reverse')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => `${placeholder(`${cols[i - 1]}-1`)} ${placeholder(`${cols[i - 1]}-2`)}`), + column_attributes: `data-story-total-columns="${cols.length}"`, + template_column_count: cols.length, + column_class: 'col--reverse', + }); + + html += `
Reversed items ${code('.row > .col.col--reverse')} - Auto column
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => `${placeholder(`${cols[i - 1]}-1`)} ${placeholder(`${cols[i - 1]}-2`)}`), + column_attributes: `data-story-total-columns="${cols.length}"`, + column_class: 'col col--reverse', + }); + + html += `
No grow ${code('.row > .col-[breakpoint]-[column].col--no-grow')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + template_column_count: cols.length, + column_class: 'col--no-grow', + }); + + html += `
No grow ${code('.row > .col.col--no-grow')} - Auto column
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + column_class: 'col col--no-grow', + }); + + html += `
No gap ${code('.row > .col-[breakpoint]-[column].col--no-gap')}
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + template_column_count: cols.length, + column_class: 'col--no-gap', + }); + + html += `
No gap ${code('.row > .col.col--no-gap')} - Auto column
`; + cols = ['A', 'B', 'C', 'D']; + html += CivicThemeGrid({ + items: generateItems(cols.length, (i) => placeholder(cols[i - 1])), + column_attributes: `data-story-total-columns="${cols.length}"`, + column_class: 'col col--no-gap', + }); + + html += '
'; + + return html; +}; + +export const GridGenerator = (parentKnobs = {}) => { + const knobs = { + number_of_items: knobNumber( + 'Number of items', + 4, + { + range: true, + min: 0, + max: 15, + step: 1, + }, + parentKnobs.number_of_items, + parentKnobs.knobTab, + ), + template_column_count: parseInt(knobRadios( + 'Template column count', + { + 'Not set (use auto columns)': '0', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 6: '6', + 12: '12', + }, + '12', + parentKnobs.template_column_count, + parentKnobs.knobTab, + ), 10), + render_as: knobRadios( + 'Render as', + { + 'div > div': 'divdiv', + 'ul > li': 'ulli', + }, + 'divdiv', + parentKnobs.render_as, + parentKnobs.knobTab, + ), + container_type: knobRadios( + 'Container type', + { + None: '', + Contained: 'contained', + Fluid: 'fluid', + }, + 'contained', + parentKnobs.container_type, + parentKnobs.knobTab, + ), + fill_width: knobBoolean('Fill width', false, parentKnobs.fill_width, parentKnobs.knobTab), + row_class: knobText('Additional row class', '', parentKnobs.row_class, 'Attributes'), + row_attributes: knobText('Additional row attributes', '', parentKnobs.row_attributes, 'Attributes'), + column_class: knobText('Additional column class', '', parentKnobs.column_class, 'Attributes'), + column_attributes: knobText('Additional column attributes', '', parentKnobs.column_attributes, 'Attributes'), + modifier_class: knobText('Additional class', '', parentKnobs.modifier_class, 'Attributes'), + attributes: knobText('Additional attributes', '', parentKnobs.attributes, 'Attributes'), + }; + + const showOutlines = knobBoolean('Show outlines', false, parentKnobs.show_outlines, parentKnobs.knobTab); + + const props = { + items: generateItems(knobs.number_of_items, () => placeholder(Math.floor(12 / (knobs.template_column_count > 0 ? knobs.template_column_count : 12)))), + row_element: knobs.render_as === 'ulli' ? 'ul' : 'div', + row_class: knobs.row_class, + row_attributes: knobs.row_attributes, + column_element: knobs.render_as === 'ulli' ? 'li' : 'div', + column_class: knobs.column_class, + column_attributes: `data-story-total-columns="${knobs.number_of_items}"`, + use_container: knobs.container_type !== 'none', + is_fluid: knobs.container_type === 'fluid', + template_column_count: knobs.template_column_count, + fill_width: knobs.fill_width, + attributes: knobs.attributes, + modifier_class: knobs.modifier_class, + }; + + return shouldRender(parentKnobs) ? `
${CivicThemeGrid(props)}
` : knobs; +}; diff --git a/components/00-base/grid/grid.stories.scss b/components/00-base/grid/grid.stories.scss index 8dc4e0c7..3437e8b1 100644 --- a/components/00-base/grid/grid.stories.scss +++ b/components/00-base/grid/grid.stories.scss @@ -2,34 +2,183 @@ // Grid component stories. // -.story-grid-wrapper { - background-color: #efefef; +@use 'sass:math'; - .container { - box-shadow: 0 0 ct-particle(0.125) ct-particle(0.125) red inset; - background-color: white; +.story-grid-outlines { + $_container-color: red; + $_fluid-container-color: purple; + $_row-color: orange; + $_template-column-color: green; + $_auto-column-color: blue; + $_placeholder-text-color: grey; + $_story-container-color: #25324d; - .row { - outline: ct-particle(0.125) solid rgba(#563d7c, 0.1); + .container, + .container-fluid { + background-color: rgba($_container-color, 0.025); + box-shadow: 0 0 1px 1px $_container-color inset; + + .row, + ul { + counter-reset: story-coloumn-counter; + background-color: rgba($_row-color, 0.2); + outline: 1px solid rgba($_row-color, 0.2); // Column. - div { - outline: ct-particle(0.125) solid rgba(blue, 0.5); - background-color: rgba(#563d7c, 0.15); - height: ct-particle(10); + > .col, + > [class*='col-'], + > li { + counter-increment: story-coloumn-counter; + position: relative; - // Column content. - span { - box-shadow: 0 0 0 ct-particle(0.125) green inset; - height: ct-particle(10); - display: flex; - align-items: center; - justify-content: center; - width: 100%; + &::after { + content: counter(story-coloumn-counter) ' of ' attr(data-story-total-columns); + position: absolute; + bottom: 2px; + right: 0; + left: 0; text-align: center; - vertical-align: middle; + font-size: 0.6em; + } + + .row, + ul { + counter-reset: story-column-nested-counter; + + > .col, + > [class*='col-'], + > li { + counter-increment: story-column-nested-counter; + + &::after { + content: 'Nested ' counter(story-column-nested-counter) ' of ' attr(data-story-total-columns); + left: auto; + font-size: 0.4em; + } + } + } + + @mixin _ct-story-placeholder-meta($width) { + position: relative; + + &::before { + content: ct-string($width); + color: $_placeholder-text-color; + position: absolute; + top: 2px; + left: 0; + right: 0; + text-align: center; + font-size: 0.6em; + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + white-space: nowrap; + } + } + + // Column content. + .story-placeholder { + @include _ct-story-placeholder-meta('100%'); + } + + .story-placeholder--hugged { + @include _ct-story-placeholder-meta('auto'); + } + + .story-placeholder--fixed { + $_width: math.div(ct-breakpoint-next($ct-grid-lowest-breakpoint), 2); + + @include _ct-story-placeholder-meta($_width); + } + } + + // Column colors. + > [class*='col-'] { + background-color: rgba($_template-column-color, 0.1); + outline: 1px solid rgba($_template-column-color, 0.5); + + &::after { + color: $_template-column-color; + } + } + + > .col, + > li { + background-color: rgba($_auto-column-color, 0.1); + outline: 1px solid rgba($_auto-column-color, 0.5); + + &::after { + color: $_auto-column-color; } } } } + + .container-fluid { + background-color: rgba($_fluid-container-color, 0.025); + box-shadow: 0 0 1px 1px $_fluid-container-color inset; + } + + &.grid-story-docs { + row-gap: 1em; + + > * { + margin-right: 1em; + white-space: nowrap; + } + + @mixin _ct-story-docs-color-square($color) { + &::before { + content: ''; + display: inline-block; + height: 1em; + width: 1em; + background-color: $color; + margin-right: 0.5em; + vertical-align: top; + } + } + + .grid-story-docs-color-container-contained { + @include _ct-story-docs-color-square($_container-color); + } + + .grid-story-docs-color-container-fluid { + @include _ct-story-docs-color-square($_fluid-container-color); + } + + .grid-story-docs-color-row { + @include _ct-story-docs-color-square($_row-color); + } + + .grid-story-docs-color-template-column { + @include _ct-story-docs-color-square($_template-column-color); + } + + .grid-story-docs-color-auto-column { + @include _ct-story-docs-color-square($_auto-column-color); + } + + .grid-story-docs-color-placeholder { + @include _ct-story-docs-color-square($_placeholder-text-color); + } + } + + .story-container__title, + .story-container__subtitle { + margin-top: 0; + margin-bottom: 0; + box-shadow: 0 0 1px 1px $_story-container-color inset; + } + + .story-container__title { + padding-bottom: ct-spacing(2); + padding-top: ct-spacing(4); + } + + .story-container__subtitle { + padding-top: ct-spacing(3); + padding-bottom: ct-spacing(2); + } } + +@include ct-story-docs-conditional('.story-grid-outlines'); diff --git a/components/00-base/grid/grid.test.js b/components/00-base/grid/grid.test.js new file mode 100644 index 00000000..a236fd93 --- /dev/null +++ b/components/00-base/grid/grid.test.js @@ -0,0 +1,317 @@ +import each from 'jest-each'; + +const template = 'components/00-base/grid/grid.twig'; + +const dataProviderGrid = () => [ + { + items: ['1'], + use_container: true, + description: 'with container', + }, + { + items: ['1'], + use_container: false, + description: 'without container', + }, + { + items: ['1', '2'], + use_container: true, + description: 'multiple items with container', + }, + { + items: ['1', '2'], + use_container: true, + is_fluid: true, + description: 'multiple items with fluid container', + }, + { + items: ['1', '2'], + use_container: false, + description: 'multiple items without container', + }, + { + items: ['1', '2', '3'], + use_container: true, + template_column_count: 3, + description: 'column count with container', + }, + { + items: ['1', '2', '3'], + use_container: true, + template_column_count: 3, + is_fluid: true, + description: 'column count with fluid container', + }, + { + items: ['1', '2', '3'], + use_container: false, + template_column_count: 3, + description: 'column count without container', + }, + { + items: ['1', '2'], + use_container: true, + row_element: 'section', + column_element: 'span', + description: 'custom elements with container', + }, + { + items: ['1', '2'], + use_container: false, + row_element: 'section', + column_element: 'span', + description: 'custom elements without container', + }, + { + items: ['1'], + use_container: true, + row_class: 'custom-row', + column_class: 'custom-col', + description: 'custom classes with container', + }, + { + items: ['1'], + use_container: false, + row_class: 'custom-row', + column_class: 'custom-col', + description: 'custom classes without container', + }, + { + items: ['1', '2'], + use_container: true, + fill_width: true, + description: 'fill width with container', + }, + { + items: ['1', '2'], + use_container: false, + fill_width: true, + description: 'fill width without container', + }, + { + items: ['1'], + use_container: true, + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + description: 'attributes and modifier class with container', + }, + { + items: ['1'], + use_container: false, + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + description: 'attributes and modifier class without container', + }, +]; + +describe('Grid', () => { + each(dataProviderGrid()).test('renders correctly %s', async (props) => { + await dom(template, props); + }); +}); + +describe('Grid Container', () => { + each([ + { use_container: true, is_fluid: false }, + { use_container: true, is_fluid: true }, + { use_container: false, is_fluid: false }, + { use_container: false, is_fluid: true }, + ]).test('renders %s', async (props) => { + const c = await dom(template, { items: ['1'], ...props }); + + if (props.use_container) { + if (props.is_fluid) { + expect(c.querySelectorAll('.container')).toHaveLength(0); + expect(c.querySelectorAll('.container-fluid')).toHaveLength(1); + } else { + expect(c.querySelectorAll('.container')).toHaveLength(1); + expect(c.querySelectorAll('.container-fluid')).toHaveLength(0); + } + } else { + expect(c.querySelectorAll('.container')).toHaveLength(0); + expect(c.querySelectorAll('.container-fluid')).toHaveLength(0); + } + }); +}); + +describe('Grid Row Fill Width', () => { + each([ + { fill_width: true }, + { fill_width: false }, + ]).test('renders %s', async (props) => { + const c = await dom(template, { + items: ['1'], + use_container: true, + ...props, + }); + + if (props.fill_width) { + expect(c.innerHTML).toContain('row--fill-width'); + } + }); +}); + +describe('Grid Attributes and Modifier Class', () => { + each([ + { + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + use_container: true, + }, + { + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + use_container: false, + }, + ]).test('renders %s', async (props) => { + const c = await dom(template, { + items: ['1'], + ...props, + }); + + if (props.use_container) { + expect(c.querySelectorAll('.container.custom-modifier')).toHaveLength(1); + expect(c.querySelectorAll('.container[data-test="true"]')).toHaveLength(1); + } else { + expect(c.querySelectorAll('.container')).toHaveLength(0); + expect(c.querySelectorAll('.row.custom-modifier')).toHaveLength(1); + expect(c.querySelectorAll('.row[data-test="true"]')).toHaveLength(1); + } + }); +}); + +describe('Grid Custom Elements', () => { + each([ + { + row_element: 'section', + column_element: 'span', + }, + { + row_element: 'div', + column_element: 'div', + }, + ]).test('renders %s', async (props) => { + const c = await dom(template, { + items: ['1'], + use_container: true, + ...props, + }); + + const rowElement = props.row_element || 'div'; + const columnElement = props.column_element || 'div'; + expect(c.querySelector(rowElement)).toBeTruthy(); + expect(c.querySelector(columnElement)).toBeTruthy(); + }); +}); + +describe('Grid Custom Classes', () => { + each([ + { + template_column_count: null, + row_class: null, + column_class: null, + fill_width: false, + expectedRowClass: 'row', + expectedColumnClass: 'col', + }, + { + template_column_count: null, + row_class: null, + column_class: null, + fill_width: true, + expectedRowClass: 'row row--fill-width', + expectedColumnClass: 'col', + }, + { + template_column_count: 0, + row_class: null, + column_class: null, + fill_width: false, + expectedRowClass: 'row', + expectedColumnClass: 'col', + }, + { + template_column_count: 0, + row_class: null, + column_class: null, + fill_width: true, + expectedRowClass: 'row row--fill-width', + expectedColumnClass: 'col', + }, + { + template_column_count: 0, + row_class: 'custom-row', + column_class: 'custom-col', + fill_width: false, + expectedRowClass: 'row custom-row', + expectedColumnClass: 'col custom-col', + }, + { + template_column_count: 0, + row_class: 'custom-row', + column_class: 'custom-col', + fill_width: true, + expectedRowClass: 'row row--fill-width custom-row', + expectedColumnClass: 'col custom-col', + }, + { + template_column_count: 12, + row_class: null, + column_class: null, + fill_width: false, + expectedRowClass: 'row', + expectedColumnClass: 'col-xxs-12 col-m-1', + }, + { + template_column_count: 12, + row_class: null, + column_class: null, + fill_width: true, + expectedRowClass: 'row row--fill-width', + expectedColumnClass: 'col-xxs-12 col-m-1', + }, + { + template_column_count: 12, + row_class: 'custom-row', + column_class: null, + fill_width: false, + expectedRowClass: 'row custom-row', + expectedColumnClass: 'col-xxs-12 col-m-1', + }, + { + template_column_count: 12, + row_class: 'custom-row', + column_class: null, + fill_width: true, + expectedRowClass: 'row row--fill-width custom-row', + expectedColumnClass: 'col-xxs-12 col-m-1', + }, + { + template_column_count: 12, + row_class: 'custom-row', + column_class: 'custom-col', + fill_width: false, + expectedRowClass: 'row custom-row', + expectedColumnClass: 'col-xxs-12 col-m-1 custom-col', + }, + { + template_column_count: 12, + row_class: 'custom-row', + column_class: 'custom-col', + fill_width: true, + expectedRowClass: 'row row--fill-width custom-row', + expectedColumnClass: 'col-xxs-12 col-m-1 custom-col', + }, + ]).test('renders %s', async (props) => { + const c = await dom(template, { + items: ['1'], + use_container: true, + ...props, + }); + + expect(c.querySelector('div:nth-child(1)>div:nth-child(1)').outerHTML) + .toContain(`class="${props.expectedRowClass}"`); + expect(c.querySelector('div:nth-child(1)>div:nth-child(1)>div:nth-child(1)').outerHTML) + .toContain(`class="${props.expectedColumnClass}"`); + }); +}); diff --git a/components/00-base/grid/grid.twig b/components/00-base/grid/grid.twig new file mode 100644 index 00000000..1e0cb68a --- /dev/null +++ b/components/00-base/grid/grid.twig @@ -0,0 +1,80 @@ +{# +/** + * @file + * Grid component. + * + * Variables: + * - items: [array] Array of column items. + * - row_element: [string] Row element. + * - row_class: [string] Optional row class override. Deffaults to 'row'. + * - row_attributes: [string] Additional row attributes. + * - column_element: [string] Column element. + * - column_class: [string] Optional column class override. Defaults to 'col' if + * column auto-generation is not required (template_column_count is 0), + * otherwise it is appended to the auto-generated column class list. + * - column_attributes: [string] Additional column attributes. + * - use_container: [boolean] Whether to use container or not. + * - is_fluid: [boolean] Whether the grid is fluid or not. Defaults to false. + * Applies only if `use_container` is true. + * - template_column_count: [number] Number of columns in the row. If 0 - the + * auto columns will be used where column width will be calculated + * automatically. Defaults to 0. + * - fill_width: [boolean] Stretch "hanging" items to the full width of the row. + * - attributes: [string] Additional attributes. + * - modifier_class: [string] Additional classes. + */ +#} + +{% set use_container = use_container|default(true) %} +{% set is_fluid = is_fluid|default(false) %} +{% set fill_width = fill_width|default(false) %} +{% set row_element = row_element|default('div') %} +{% set column_element = column_element|default('div') %} +{% set template_column_count = template_column_count|default(0) %} +{% set row_class = row_class|default('') %} +{% set modifier_class = modifier_class|default('') %} + +{% if template_column_count > 0 %} + {% set column_class = column_class|default('') %} + {% set template_column_count = template_column_count in [1, 2, 3, 4, 6, 12] ? template_column_count : 12 %} + {% set column_class = 'col-xxs-12 col-m-%s %s'|format(12 // template_column_count, column_class)|trim %} +{% else %} + {% set column_class = ('col ' ~ column_class|default(''))|trim %} +{% endif %} + +{% set row_classes = ['row'] %} +{% if fill_width %} + {% set row_classes = row_classes|merge(['row--fill-width']) %} +{% endif %} +{% set row_classes = row_classes|merge([row_class]) %} + +{% set container_class = '' %} +{% if use_container %} + {% set container_class = ((is_fluid ? 'container-fluid' : 'container') ~ ' ' ~ modifier_class)|trim %} +{% else %} + {# Propagade modifier class to row class if container is not used. #} + {% if modifier_class is not empty %} + {% set row_classes = row_classes|merge([modifier_class]) %} + {% endif %} + {% set row_attributes = (row_attributes ~ ' ' ~ attributes)|trim %} +{% endif %} + +{% set row_class_string = row_classes|join(' ')|trim %} + +{% if items is not empty %} + {% if use_container %} +
+ {% endif %} + <{{ row_element }} class="{{ row_class_string }}" {% if row_attributes is not empty %}{{ row_attributes|raw }}{% endif %}> + {% for item in items %} + {% if item is not empty %} + <{{ column_element }} class="{{ column_class }}" {% if column_attributes is not empty %}{{ column_attributes|raw }}{% endif %}> + {{ item|raw }} + + {% endif %} + {% endfor %} + + {% if use_container %} +
+ {% endif %} +{% endif %} diff --git a/components/00-base/icon/__snapshots__/icon.test.js.snap b/components/00-base/icon/__snapshots__/icon.test.js.snap new file mode 100644 index 00000000..1aa1f797 --- /dev/null +++ b/components/00-base/icon/__snapshots__/icon.test.js.snap @@ -0,0 +1,99 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Icon Component does not render when symbol is empty 1`] = ` +
+ + + + +
+`; + +exports[`Icon Component renders with additional attributes and classes 1`] = ` +
+ + + + + + + + +
+`; + +exports[`Icon Component renders with custom size 1`] = ` +
+ + + + + + + + +
+`; + +exports[`Icon Component renders with default values 1`] = ` +
+ + + + + + + + +
+`; diff --git a/components/00-base/icon/icon.stories.js b/components/00-base/icon/icon.stories.js index e10e6223..7c01d5ec 100644 --- a/components/00-base/icon/icon.stories.js +++ b/components/00-base/icon/icon.stories.js @@ -1,8 +1,6 @@ -import { radios, select, text } from '@storybook/addon-knobs'; - import merge from 'deepmerge'; import CivicThemeIcon from './icon.twig'; -import { arrayCombine, toLabels } from '../base.utils'; +import { arrayCombine, knobRadios, knobSelect, knobText, shouldRender, toLabels } from '../storybook/storybook.utils'; export default { title: 'Base/Icon', @@ -11,9 +9,7 @@ export default { }, }; -export const Icon = (knobTab) => { - const generalKnobTab = typeof knobTab === 'string' ? knobTab : 'General'; - +export const Icon = (parentKnobs = {}) => { const defaultSizes = SCSS_VARIABLES['ct-icon-sizes-default']; const customSizes = SCSS_VARIABLES['ct-icon-sizes']; let sizes = Object.keys(merge(defaultSizes, customSizes)); @@ -21,11 +17,13 @@ export const Icon = (knobTab) => { sizes = arrayCombine(toLabels(sizes), sizes); sizes = merge({ Auto: 'auto' }, sizes); - return CivicThemeIcon({ - symbol: select('Symbol', ICONS, ICONS[0], generalKnobTab), - alt: text('Alt', 'Icon alt text', generalKnobTab), - size: radios('Size', sizes, 'auto', generalKnobTab), - modifier_class: text('Additional classes', '', generalKnobTab), - attributes: text('Additional attributes', '', generalKnobTab), - }); + const knobs = { + symbol: knobSelect('Symbol', ICONS, ICONS[0], parentKnobs.symbol, parentKnobs.knobTab), + alt: knobText('Alt', 'Icon alt text', parentKnobs.alt, parentKnobs.knobTab), + size: knobRadios('Size', sizes, 'auto', parentKnobs.size, parentKnobs.knobTab), + modifier_class: knobText('Additional classes', '', parentKnobs.modifier_class, parentKnobs.knobTab), + attributes: knobText('Additional attributes', '', parentKnobs.attributes, parentKnobs.knobTab), + }; + + return shouldRender(parentKnobs) ? CivicThemeIcon(knobs) : knobs; }; diff --git a/components/00-base/icon/icon.test.js b/components/00-base/icon/icon.test.js new file mode 100644 index 00000000..b764eb4e --- /dev/null +++ b/components/00-base/icon/icon.test.js @@ -0,0 +1,53 @@ +const template = 'components/00-base/icon/icon.twig'; + +describe('Icon Component', () => { + test('renders with default values', async () => { + const c = await dom(template, { + symbol: 'close', + }); + + expect(c.querySelectorAll('.ct-icon')).toHaveLength(1); + expect(c.querySelectorAll('.ct-icon[aria-hidden="true"]')).toHaveLength(1); + expect(c.querySelectorAll('.ct-icon[role="img"]')).toHaveLength(1); + + assertUniqueCssClasses(c); + }); + + test('renders with custom size', async () => { + const c = await dom(template, { + symbol: 'close', + size: 'large', + alt: 'Test Icon', + }); + + expect(c.querySelectorAll('.ct-icon.ct-icon--size-large')).toHaveLength(1); + expect(c.querySelectorAll('.ct-icon[aria-hidden="true"]')).toHaveLength(1); + expect(c.querySelectorAll('.ct-icon[role="img"]')).toHaveLength(1); + expect(c.querySelectorAll('.ct-icon[alt="Test Icon"]')).toHaveLength(1); + + assertUniqueCssClasses(c); + }); + + test('renders with additional attributes and classes', async () => { + const c = await dom(template, { + symbol: 'close', + modifier_class: 'custom-modifier', + attributes: 'data-test="true"', + }); + + expect(c.querySelectorAll('.ct-icon.custom-modifier')).toHaveLength(1); + expect(c.querySelectorAll('.ct-icon[data-test="true"]')).toHaveLength(1); + + assertUniqueCssClasses(c); + }); + + test('does not render when symbol is empty', async () => { + const c = await dom(template, { + symbol: '', + }); + + expect(c.querySelectorAll('.ct-icon')).toHaveLength(0); + + assertUniqueCssClasses(c); + }); +}); diff --git a/components/00-base/icon/icon.twig b/components/00-base/icon/icon.twig index b22d4bef..7af84d0f 100644 --- a/components/00-base/icon/icon.twig +++ b/components/00-base/icon/icon.twig @@ -18,7 +18,13 @@ {% if symbol is not empty %} {% set source = source(assets_dir ~ '/icons/' ~ symbol ~ '.svg', true) %} {% if source is not empty %} - {% set attributes = 'class="ct-icon ' ~ (size ? 'ct-icon--size-' ~ size : '') ~ ' ' ~ modifier_class|default('') ~ '" aria-hidden="true" role="img" ' ~ (alt is defined ? 'alt="' ~ alt ~ '"' : '') ~ attributes|default('') %} + {% set size_class = size ? 'ct-icon--size-' ~ size : '' %} + {% set base_class = 'ct-icon ' ~ size_class %} + {% set modifier = modifier_class|default('') %} + {% set aria_attributes = 'aria-hidden="true" role="img"' %} + {% set alt_attribute = alt is defined ? 'alt="' ~ alt ~ '"' : '' %} + {% set additional_attributes = attributes|default('') %} + {% set attributes = 'class="' ~ base_class ~ ' ' ~ modifier ~ '" ' ~ aria_attributes ~ ' ' ~ alt_attribute ~ ' ' ~ additional_attributes %} {{ source|replace({' { - const generalKnobTab = typeof knobTab === 'string' ? knobTab : 'General'; - - const generalKnobs = { - column_count: number( - 'Columns', - 4, - { - range: true, - min: 0, - max: 4, - step: 1, - }, - generalKnobTab, - ), - fill_width: boolean('Fill width', false, generalKnobTab), - items: generateItems(number( - 'Number of items', - 4, - { - range: true, - min: 0, - max: 7, - step: 1, - }, - generalKnobTab, - ), placeholder()), - modifier_class: text('Additional class', '', generalKnobTab), - }; - - return CivicThemeItemGrid({ - ...generalKnobs, - }); -}; diff --git a/components/00-base/item-grid/item-grid.twig b/components/00-base/item-grid/item-grid.twig deleted file mode 100644 index 4540a0fb..00000000 --- a/components/00-base/item-grid/item-grid.twig +++ /dev/null @@ -1,37 +0,0 @@ -{# -/** - * @file - * Item grid component. - * - * Variables: - * - items: [array] Items array. - * - column_count: [number] Number of columns in the container. - * - fill_width: [boolean] Stretch "hanging" items to the full width of container or not. - * - attributes: [string] Additional attributes. - * - modifier_class: [string] Additional classes. - */ -#} - -{% set column_class = 'col-xxs-12 col-m-%s'|format(12 // column_count|default(1)) %} -{% set fill_width_class = fill_width ? 'ct-item-grid--fill-width' : '' %} -{% set modifier_class = '%s %s'|format(fill_width_class, modifier_class|default('')) %} - -{% if items is not empty %} -
-
-
-
-
    - {% for item in items %} - {% if item is not empty %} -
  • - {{ item }} -
  • - {% endif %} - {% endfor %} -
-
-
-
-
-{% endif %} diff --git a/components/00-base/item-list/__snapshots__/item-list.test.js.snap b/components/00-base/item-list/__snapshots__/item-list.test.js.snap new file mode 100644 index 00000000..5150873c --- /dev/null +++ b/components/00-base/item-list/__snapshots__/item-list.test.js.snap @@ -0,0 +1,255 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Link List Component does not render when items are empty 1`] = ` +
+ + + +
+`; + +exports[`Link List Component renders with custom attributes and classes 1`] = ` +
+ + + + + + +
+`; + +exports[`Link List Component renders with default values 1`] = ` +
+ + + + + + +
+`; + +exports[`Link List Component renders with large size 1`] = ` +
+ + + + + + +
+`; + +exports[`Link List Component renders with vertical direction 1`] = ` +
+ + + + + + +
+`; + +exports[`Link List Component renders without gaps 1`] = ` +
+ + + + + + +
+`; diff --git a/components/00-base/item-list/item-list.scss b/components/00-base/item-list/item-list.scss index 1b920286..72a6ac2d 100644 --- a/components/00-base/item-list/item-list.scss +++ b/components/00-base/item-list/item-list.scss @@ -2,6 +2,7 @@ // Item List component. // +@use 'sass:math'; @import '../mixins/reset'; .ct-item-list { @@ -13,9 +14,9 @@ &#{$root}--horizontal { display: flex; + flex-wrap: wrap; column-gap: $ct-item-list-horizontal-regular-column-gap; row-gap: $ct-item-list-horizontal-regular-row-gap; - flex-wrap: wrap; &#{$root}--small { column-gap: $ct-item-list-horizontal-small-column-gap; @@ -23,8 +24,13 @@ } &#{$root}--large { - column-gap: $ct-item-list-horizontal-large-column-gap; - row-gap: $ct-item-list-horizontal-large-row-gap; + column-gap: math.div($ct-item-list-horizontal-large-column-gap, 2); + row-gap: math.div($ct-item-list-horizontal-large-row-gap, 2); + + @include ct-breakpoint($ct-grid-responsive-breakpoint) { + column-gap: $ct-item-list-horizontal-large-column-gap; + row-gap: $ct-item-list-horizontal-large-row-gap; + } } &#{$root}--no-gap { @@ -44,8 +50,13 @@ } &#{$root}--large { - column-gap: $ct-item-list-vertical-large-column-gap; - row-gap: $ct-item-list-vertical-large-row-gap; + column-gap: math.div($ct-item-list-vertical-large-column-gap, 2); + row-gap: math.div($ct-item-list-vertical-large-row-gap, 2); + + @include ct-breakpoint($ct-grid-responsive-breakpoint) { + column-gap: $ct-item-list-vertical-large-column-gap; + row-gap: $ct-item-list-vertical-large-row-gap; + } } &#{$root}--no-gap { diff --git a/components/00-base/item-list/item-list.stories.js b/components/00-base/item-list/item-list.stories.js index 847ddc36..157f58df 100644 --- a/components/00-base/item-list/item-list.stories.js +++ b/components/00-base/item-list/item-list.stories.js @@ -1,31 +1,27 @@ -import { - boolean, number, radios, text, -} from '@storybook/addon-knobs'; - import CivicThemeItemList from './item-list.twig'; -import { generateItems, placeholder, randomSentence } from '../base.utils'; +import { generateItems, knobBoolean, knobNumber, knobRadios, knobText, placeholder, randomSentence, shouldRender } from '../storybook/storybook.utils'; export default { title: 'Base/Item List', parameters: { layout: 'centered', + storyLayoutSize: 'large', }, }; -export const ItemList = (knobTab) => { - const generalKnobTab = typeof knobTab === 'string' ? knobTab : 'General'; - - const generalKnobs = { - direction: radios( +export const ItemList = (parentKnobs = {}) => { + const knobs = { + direction: knobRadios( 'Direction', { Horizontal: 'horizontal', Vertical: 'vertical', }, 'horizontal', - generalKnobTab, + parentKnobs.direction, + parentKnobs.knobTab, ), - size: radios( + size: knobRadios( 'Size', { Large: 'large', @@ -33,10 +29,11 @@ export const ItemList = (knobTab) => { Small: 'small', }, 'regular', - generalKnobTab, + parentKnobs.size, + parentKnobs.knobTab, ), - no_gap: boolean('No gap', false, generalKnobTab), - items: generateItems(number( + no_gap: knobBoolean('No gap', false, parentKnobs.no_gap, parentKnobs.knobTab), + items_count: knobNumber( 'Items count', 5, { @@ -45,13 +42,17 @@ export const ItemList = (knobTab) => { max: 10, step: 1, }, - generalKnobTab, - ), placeholder(boolean('Long placeholder text', false, generalKnobTab) ? randomSentence(30) : 'Content placeholder')), - attributes: text('Additional attributes', '', generalKnobTab), - modifier_class: `story-wrapper-size--large ${text('Additional class', '', generalKnobTab)}`, + parentKnobs.items_count, + parentKnobs.knobTab, + ), + long_placeholder_text: knobBoolean('Long placeholder text', false, parentKnobs.long_placeholder_text, parentKnobs.knobTab), + attributes: knobText('Additional attributes', '', parentKnobs.attributes, parentKnobs.knobTab), + modifier_class: knobText('Additional class', '', parentKnobs.modifier_class, parentKnobs.knobTab), }; + knobs.items = generateItems( + knobs.items_count, + placeholder(knobs.long_placeholder_text ? randomSentence(30) : 'Content placeholder'), + ); - return CivicThemeItemList({ - ...generalKnobs, - }); + return shouldRender(parentKnobs) ? CivicThemeItemList(knobs) : knobs; }; diff --git a/components/00-base/item-list/item-list.test.js b/components/00-base/item-list/item-list.test.js new file mode 100644 index 00000000..fd9866fb --- /dev/null +++ b/components/00-base/item-list/item-list.test.js @@ -0,0 +1,94 @@ +const template = 'components/00-base/item-list/item-list.twig'; + +describe('Link List Component', () => { + test('renders with default values', async () => { + const c = await dom(template, { + items: [ + 'Home', + 'About', + 'Contact', + ], + }); + + expect(c.querySelectorAll('.ct-item-list')).toHaveLength(1); + expect(c.querySelectorAll('.ct-item-list__item')).toHaveLength(3); + expect(c.querySelector('.ct-item-list__item').textContent.trim()).toEqual('Home'); + expect(c.querySelector('.ct-item-list__item:nth-child(2)').textContent.trim()).toEqual('About'); + expect(c.querySelector('.ct-item-list__item:nth-child(3)').textContent.trim()).toEqual('Contact'); + + assertUniqueCssClasses(c); + }); + + test('renders with vertical direction', async () => { + const c = await dom(template, { + direction: 'vertical', + items: [ + 'Home', + 'About', + 'Contact', + ], + }); + + expect(c.querySelectorAll('.ct-item-list.ct-item-list--vertical')).toHaveLength(1); + expect(c.querySelectorAll('.ct-item-list__item')).toHaveLength(3); + + assertUniqueCssClasses(c); + }); + + test('renders with large size', async () => { + const c = await dom(template, { + size: 'large', + items: [ + 'Home', + 'About', + 'Contact', + ], + }); + + expect(c.querySelectorAll('.ct-item-list.ct-item-list--large')).toHaveLength(1); + expect(c.querySelectorAll('.ct-item-list__item')).toHaveLength(3); + + assertUniqueCssClasses(c); + }); + + test('renders without gaps', async () => { + const c = await dom(template, { + no_gap: true, + items: [ + 'Home', + 'About', + 'Contact', + ], + }); + + expect(c.querySelectorAll('.ct-item-list.ct-item-list--no-gap')).toHaveLength(1); + expect(c.querySelectorAll('.ct-item-list__item')).toHaveLength(3); + + assertUniqueCssClasses(c); + }); + + test('renders with custom attributes and classes', async () => { + const c = await dom(template, { + items: [ + 'Home', + 'About', + 'Contact', + ], + attributes: 'data-test="true"', + modifier_class: 'custom-modifier', + }); + + expect(c.querySelectorAll('.ct-item-list.custom-modifier')).toHaveLength(1); + expect(c.querySelectorAll('.ct-item-list[data-test="true"]')).toHaveLength(1); + + assertUniqueCssClasses(c); + }); + + test('does not render when items are empty', async () => { + const c = await dom(template, { + items: [], + }); + + expect(c.querySelectorAll('.ct-item-list')).toHaveLength(0); + }); +}); diff --git a/components/00-base/item-list/item-list.twig b/components/00-base/item-list/item-list.twig index 0e33fd3d..0183cc0e 100644 --- a/components/00-base/item-list/item-list.twig +++ b/components/00-base/item-list/item-list.twig @@ -21,11 +21,11 @@ {% set modifier_class = '%s %s %s %s'|format(direction_class, size_class, no_gap_class, modifier_class|default('')) %} {% if items %} -