From 3dd37ec490859c41a8168f7921e93f7fbf17501b Mon Sep 17 00:00:00 2001 From: James Hall Date: Tue, 22 Oct 2024 08:05:15 +0100 Subject: [PATCH] feat: add new header and footer components BREAKING CHANGE: consumer html for `.iati-header` and `.iati-footer` components must be updated BREAKING CHANGE: `.iati-title-bar` component has been removed --- .storybook/main.ts | 1 + .storybook/preview.ts | 6 + src/assets/svg/icon-globe.svg | 5 + src/assets/svg/icon-info.svg | 5 + src/assets/svg/icon-search.svg | 3 + src/scss/base/_focus.scss | 6 + src/scss/base/_index.scss | 1 + src/scss/base/_mixins.scss | 18 ++- src/scss/base/_reset.scss | 6 +- src/scss/components/_index.scss | 12 ++ .../brand-background/_brand-background.scss | 22 ++++ .../brand-background.stories.ts | 19 ++++ .../components/breadcrumb/_breadcrumb.scss | 54 +++++++++ .../breadcrumb/breadcrumb.stories.ts | 39 +++++++ src/scss/components/button/_button.scss | 9 ++ src/scss/components/button/button.stories.ts | 9 +- src/scss/components/callout/_callout.scss | 2 +- .../country-switcher/_country-switcher.scss | 40 +++++++ .../country-switcher.stories.ts | 28 +++++ .../footer-block/_footer-block.scss | 50 +++++++++ .../footer-block/footer-block.stories.ts | 53 +++++++++ .../header-button/_header-button.scss | 25 +++++ .../header-button/header-button.stories.ts | 35 ++++++ .../components/menu-toggle/_menu-toggle.scss | 36 ++++++ .../menu-toggle/menu-toggle.stories.ts | 34 ++++++ src/scss/components/message/_message.scss | 27 +++++ .../components/message/message.stories.ts | 32 ++++++ .../components/mobile-nav/_mobile-nav.scss | 89 +++++++++++++++ .../mobile-nav/mobile-nav.stories.ts | 59 ++++++++++ .../newsletter-form/_newsletter-form.scss | 44 ++++++++ .../newsletter-form.stories.ts | 58 ++++++++++ .../components/piped-list/_piped-list.scss | 28 +++++ .../piped-list/piped-list.stories.ts | 20 ++++ .../components/search-bar/_search-bar.scss | 2 +- src/scss/components/title-bar/_title-bar.scss | 39 +++++++ .../components/title-bar/title-bar.stories.ts | 23 ++++ src/scss/components/tool-nav/_tool-nav.scss | 26 +++++ .../components/tool-nav/tool-nav.stories.ts | 29 +++++ src/scss/layout/_index.scss | 1 - src/scss/layout/footer/_footer.scss | 76 ++++++++----- src/scss/layout/footer/footer.stories.ts | 85 ++++++++------ src/scss/layout/header/_header.scss | 106 +++++++++++------- src/scss/layout/header/header.stories.ts | 100 ++++++++++++++--- src/scss/layout/page/_page.scss | 4 + src/scss/layout/page/page.stories.ts | 26 ++++- src/scss/layout/title-bar/_title-bar.scss | 53 --------- .../layout/title-bar/title-bar.stories.ts | 29 ----- static/images/marque-white.svg | 59 ++++++++++ static/js/header.js | 67 +++++++++++ 49 files changed, 1386 insertions(+), 214 deletions(-) create mode 100644 src/assets/svg/icon-globe.svg create mode 100644 src/assets/svg/icon-info.svg create mode 100644 src/assets/svg/icon-search.svg create mode 100644 src/scss/base/_focus.scss create mode 100644 src/scss/components/brand-background/_brand-background.scss create mode 100644 src/scss/components/brand-background/brand-background.stories.ts create mode 100644 src/scss/components/breadcrumb/_breadcrumb.scss create mode 100644 src/scss/components/breadcrumb/breadcrumb.stories.ts create mode 100644 src/scss/components/country-switcher/_country-switcher.scss create mode 100644 src/scss/components/country-switcher/country-switcher.stories.ts create mode 100644 src/scss/components/footer-block/_footer-block.scss create mode 100644 src/scss/components/footer-block/footer-block.stories.ts create mode 100644 src/scss/components/header-button/_header-button.scss create mode 100644 src/scss/components/header-button/header-button.stories.ts create mode 100644 src/scss/components/menu-toggle/_menu-toggle.scss create mode 100644 src/scss/components/menu-toggle/menu-toggle.stories.ts create mode 100644 src/scss/components/message/_message.scss create mode 100644 src/scss/components/message/message.stories.ts create mode 100644 src/scss/components/mobile-nav/_mobile-nav.scss create mode 100644 src/scss/components/mobile-nav/mobile-nav.stories.ts create mode 100644 src/scss/components/newsletter-form/_newsletter-form.scss create mode 100644 src/scss/components/newsletter-form/newsletter-form.stories.ts create mode 100644 src/scss/components/piped-list/_piped-list.scss create mode 100644 src/scss/components/piped-list/piped-list.stories.ts create mode 100644 src/scss/components/title-bar/_title-bar.scss create mode 100644 src/scss/components/title-bar/title-bar.stories.ts create mode 100644 src/scss/components/tool-nav/_tool-nav.scss create mode 100644 src/scss/components/tool-nav/tool-nav.stories.ts delete mode 100644 src/scss/layout/title-bar/_title-bar.scss delete mode 100644 src/scss/layout/title-bar/title-bar.stories.ts create mode 100644 static/images/marque-white.svg create mode 100644 static/js/header.js diff --git a/.storybook/main.ts b/.storybook/main.ts index 730dd9d..ccc75e6 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -5,6 +5,7 @@ const config: StorybookConfig = { framework: "@storybook/web-components-vite", stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], addons: ["@storybook/addon-essentials"], + staticDirs: ["../static"], docs: { autodocs: true, }, diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 82f8158..67942ed 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -10,6 +10,12 @@ const preview: Preview = { order: ["Get Started", "Brand", "Core", "Components", "Layout"], }, }, + backgrounds: { + values: [ + { name: "light", value: "#fff" }, + { name: "dark", value: "#155366" }, + ], + }, docs: { page: DocsTemplate, source: { diff --git a/src/assets/svg/icon-globe.svg b/src/assets/svg/icon-globe.svg new file mode 100644 index 0000000..592ebc1 --- /dev/null +++ b/src/assets/svg/icon-globe.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svg/icon-info.svg b/src/assets/svg/icon-info.svg new file mode 100644 index 0000000..02f6aba --- /dev/null +++ b/src/assets/svg/icon-info.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svg/icon-search.svg b/src/assets/svg/icon-search.svg new file mode 100644 index 0000000..d7870d5 --- /dev/null +++ b/src/assets/svg/icon-search.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/scss/base/_focus.scss b/src/scss/base/_focus.scss new file mode 100644 index 0000000..8ee9a37 --- /dev/null +++ b/src/scss/base/_focus.scss @@ -0,0 +1,6 @@ +@use "../tokens/color" as *; + +:is(a, button, input, select, textarea, summary):focus-visible { + outline: 2px solid $color-blue-60; + outline-offset: 2px; +} diff --git a/src/scss/base/_index.scss b/src/scss/base/_index.scss index 85802a3..b3714c1 100644 --- a/src/scss/base/_index.scss +++ b/src/scss/base/_index.scss @@ -1,3 +1,4 @@ @forward "normalize"; @forward "reset"; @forward "mixins"; +@forward "focus"; diff --git a/src/scss/base/_mixins.scss b/src/scss/base/_mixins.scss index 9165a69..12353af 100644 --- a/src/scss/base/_mixins.scss +++ b/src/scss/base/_mixins.scss @@ -4,9 +4,9 @@ @mixin page-width-container { width: 100%; max-width: $page-width-max; - margin-left: auto; + margin-inline-start: auto; margin-right: auto; - padding: $padding-block; + padding: 0 $padding-block; } @mixin columns { @@ -30,6 +30,18 @@ } li li { - padding-left: $padding-block; + padding-inline-start: $padding-block; } } + +@mixin sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} diff --git a/src/scss/base/_reset.scss b/src/scss/base/_reset.scss index 4e3dbf1..aaac052 100644 --- a/src/scss/base/_reset.scss +++ b/src/scss/base/_reset.scss @@ -4,7 +4,7 @@ box-sizing: border-box; } -a, +a[href], button { cursor: pointer; } @@ -23,3 +23,7 @@ button, input { font-weight: inherit; } + +input { + border-radius: 0; +} diff --git a/src/scss/components/_index.scss b/src/scss/components/_index.scss index fbea8e9..78753a0 100644 --- a/src/scss/components/_index.scss +++ b/src/scss/components/_index.scss @@ -1,6 +1,18 @@ @forward "button/button"; +@forward "breadcrumb/breadcrumb"; +@forward "brand-background/brand-background"; @forward "callout/callout"; @forward "card/card"; +@forward "country-switcher/country-switcher"; +@forward "piped-list/piped-list"; +@forward "header-button/header-button"; @forward "icon/icon"; @forward "search-bar/search-bar"; @forward "table/table"; +@forward "tool-nav/tool-nav"; +@forward "menu-toggle/menu-toggle"; +@forward "message/message"; +@forward "mobile-nav/mobile-nav"; +@forward "newsletter-form/newsletter-form"; +@forward "title-bar/title-bar"; +@forward "footer-block/footer-block"; diff --git a/src/scss/components/brand-background/_brand-background.scss b/src/scss/components/brand-background/_brand-background.scss new file mode 100644 index 0000000..6797345 --- /dev/null +++ b/src/scss/components/brand-background/_brand-background.scss @@ -0,0 +1,22 @@ +@use "../../tokens/color" as *; +@use "../../tokens/screens" as *; + +.iati-brand-background { + background-color: $color-teal-90; + display: grid; + @media (min-width: $screen-md) { + &:after { + content: ""; + grid-area: 1/-1; + background-image: url("/images/marque-white.svg"); + background-repeat: no-repeat; + background-position: right 2rem top; + background-size: 32.3rem auto; + opacity: 0.1; + } + .iati-brand-background__content { + grid-area: 1/-1; + z-index: 1; + } + } +} diff --git a/src/scss/components/brand-background/brand-background.stories.ts b/src/scss/components/brand-background/brand-background.stories.ts new file mode 100644 index 0000000..3385eac --- /dev/null +++ b/src/scss/components/brand-background/brand-background.stories.ts @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Brand Background", +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => + html`
+
+

Some words

+
+
`, +}; diff --git a/src/scss/components/breadcrumb/_breadcrumb.scss b/src/scss/components/breadcrumb/_breadcrumb.scss new file mode 100644 index 0000000..8f56895 --- /dev/null +++ b/src/scss/components/breadcrumb/_breadcrumb.scss @@ -0,0 +1,54 @@ +@use "../../tokens/color" as *; +@use "../../tokens/spacing" as *; +@use "../../tokens/screens" as *; + +.iati-breadcrumb { + font-weight: 600; +} + +.iati-breadcrumb__list { + list-style-type: none; + padding: 0; + margin: 0; + display: flex; + display: none; + > *:not(:last-child)::after { + content: "/"; + color: $color-teal-90; + font-weight: 600; + display: inline-block; + padding-inline: 0.4em; + } +} + +.iati-breadcrumb-item { + line-height: 1; +} + +.iati-breadcrumb__previous { + display: flex; + align-items: flex-end; + &::before { + content: ""; + display: inline-block; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor' class='size-5'%3E%3Cpath fill-rule='evenodd' d='M11.78 5.22a.75.75 0 0 1 0 1.06L8.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z' clip-rule='evenodd' /%3E%3C/svg%3E%0A"); + background-size: contain; + background-position: center; + height: 1.2em; + width: 1.2em; + } +} + +.iati-breadcrumb-link { + text-decoration: none; + line-height: 1; +} + +@media (min-width: $screen-sm) { + .iati-breadcrumb__previous { + display: none; + } + .iati-breadcrumb__list { + display: flex; + } +} diff --git a/src/scss/components/breadcrumb/breadcrumb.stories.ts b/src/scss/components/breadcrumb/breadcrumb.stories.ts new file mode 100644 index 0000000..2e47182 --- /dev/null +++ b/src/scss/components/breadcrumb/breadcrumb.stories.ts @@ -0,0 +1,39 @@ +import { html } from "lit"; +import { classMap } from "lit-html/directives/class-map.js"; + +import type { Meta, StoryObj } from "@storybook/web-components"; + +const items = ["Home", "About", "Current page"]; + +const meta: Meta = { + title: "Components/Breadcrumb", +}; + +export default meta; +type Story = StoryObj; + +export const Breadcrumb: Story = { + render: () => html` + + `, +}; diff --git a/src/scss/components/button/_button.scss b/src/scss/components/button/_button.scss index f084647..f5015ce 100644 --- a/src/scss/components/button/_button.scss +++ b/src/scss/components/button/_button.scss @@ -12,3 +12,12 @@ background-color: $color-teal-80; } } + +.iati-button--submit { + color: $color-grey-90; + font-weight: 600; + background-color: $color-green-50; + &:hover { + background-color: $color-green-40; + } +} diff --git a/src/scss/components/button/button.stories.ts b/src/scss/components/button/button.stories.ts index f2d362a..84b6b31 100644 --- a/src/scss/components/button/button.stories.ts +++ b/src/scss/components/button/button.stories.ts @@ -9,6 +9,11 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Button: Story = { - render: () => html``, +export const Default: Story = { + render: () => html``, +}; + +export const Submit: Story = { + render: () => + html``, }; diff --git a/src/scss/components/callout/_callout.scss b/src/scss/components/callout/_callout.scss index 76a014e..30b10f2 100644 --- a/src/scss/components/callout/_callout.scss +++ b/src/scss/components/callout/_callout.scss @@ -4,7 +4,7 @@ .iati-callout { padding: $padding-block; - border-left: $border-width solid $color-blue-50; + border-inline-start: $border-width solid $color-blue-50; background-color: $color-teal-10; color: $color-teal-90; box-shadow: 0 4px 5px $color-grey-20; diff --git a/src/scss/components/country-switcher/_country-switcher.scss b/src/scss/components/country-switcher/_country-switcher.scss new file mode 100644 index 0000000..08b05d3 --- /dev/null +++ b/src/scss/components/country-switcher/_country-switcher.scss @@ -0,0 +1,40 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; +@use "../../base/mixins"; + +.iati-country-switcher { + background-color: #fff; + position: relative; + display: inline-block; + color: #121212; + cursor: pointer; + .iati-country-switcher__label { + @include mixins.sr-only; + } + .iati-country-switcher__control { + padding: 0.7em 2.1em 0.7em calc(1em + 1rem); + font-family: $font-stack-heading; + line-height: 1.1; + width: 100%; + height: 100%; + appearance: none; + background-color: #fff; + border: none; + font-family: $font-stack-heading; + color: $color-grey-90; + font-weight: 600; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='none'%3E%3Cpath stroke='%23121212' stroke-linecap='square' stroke-width='1.3' d='M10 19c4.9706 0 9-4.0294 9-9 0-4.97056-4.0294-9-9-9-4.97056 0-9 4.02944-9 9 0 4.9706 4.02944 9 9 9Z'/%3E%3Cpath stroke='%23121212' stroke-linecap='round' stroke-linejoin='bevel' stroke-width='1.3' d='M9.99984 18.991C12.3938 16.8114 13.5908 13.8144 13.5908 10c0-3.81433-1.197-6.81133-3.59096-8.99097-2.394 2.17964-3.591 5.17664-3.591 8.99097 0 3.8144 1.197 6.8114 3.591 8.991Z'/%3E%3Cpath stroke='%23121212' stroke-linecap='round' stroke-width='1.3' d='M1.44991 7.29993H18.5499M1.44991 12.6999H18.5499'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: 0.5em center; + } + &::after { + position: absolute; + right: 0.7em; + top: calc(50% - 0.1875em); + content: ""; + width: 0.75em; + height: 0.375em; + background-color: currentColor; + clip-path: polygon(15% 0, 0 0, 50% 100%, 100% 0, 85% 0, 50% 70%); + } +} diff --git a/src/scss/components/country-switcher/country-switcher.stories.ts b/src/scss/components/country-switcher/country-switcher.stories.ts new file mode 100644 index 0000000..5bf6f97 --- /dev/null +++ b/src/scss/components/country-switcher/country-switcher.stories.ts @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Country Switcher", + parameters: { + backgrounds: { + default: "dark", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const CountrySwitcher: Story = { + render: () => html` +
+ + +
+ `, +}; diff --git a/src/scss/components/footer-block/_footer-block.scss b/src/scss/components/footer-block/_footer-block.scss new file mode 100644 index 0000000..18b719b --- /dev/null +++ b/src/scss/components/footer-block/_footer-block.scss @@ -0,0 +1,50 @@ +@use "../../tokens/screens" as *; +@use "../../tokens/font" as *; +@use "../../tokens/color" as *; +@use "../../base/mixins"; + +.iati-footer-block { + color: #fff; +} + +.iati-footer-block__title { + margin: 0 0 1.5rem; + font-family: $font-stack-heading; + font-size: 1.25rem; + line-height: 1.2; + font-weight: 700; + &--centered { + @media (min-width: $screen-md) { + text-align: center; + } + } +} + +.iati-footer-block__content { + font-weight: 600; + :where(p, li) { + line-height: 1.375; + margin: 0; + } + a { + color: currentColor; + &:hover { + color: $color-blue-30; + } + } + ul { + list-style-type: none; + margin: 0; + padding: 0; + } + > * + * { + margin-block-start: 0.5rem; + } +} + +.iati-footer-block__content--columns { + @media (min-width: $screen-sm) { + columns: 3; + column-gap: 2rem; + } +} diff --git a/src/scss/components/footer-block/footer-block.stories.ts b/src/scss/components/footer-block/footer-block.stories.ts new file mode 100644 index 0000000..6a4e4dc --- /dev/null +++ b/src/scss/components/footer-block/footer-block.stories.ts @@ -0,0 +1,53 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Footer/Footer Block", + parameters: { + backgrounds: { + default: "dark", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const UsefulLinks: Story = { + render: () => + html``, +}; + +export const AdditionInfo: Story = { + render: () => + html``, +}; diff --git a/src/scss/components/header-button/_header-button.scss b/src/scss/components/header-button/_header-button.scss new file mode 100644 index 0000000..3696821 --- /dev/null +++ b/src/scss/components/header-button/_header-button.scss @@ -0,0 +1,25 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; +@use "../../tokens/spacing" as *; +@use "../../tokens/screens" as *; + +.header-button { + display: inline-flex; + padding: 0.7em; + font-family: $font-stack-heading; + color: $color-grey-90; + font-weight: 600; + align-items: center; + gap: 0.25em; + background-color: #fff; + transition: all 0.2s ease-in-out; + text-decoration: none; + line-height: 1.1; + &:hover { + background-color: $color-blue-30; + } +} + +.header-button__icon { + width: 1rem; +} diff --git a/src/scss/components/header-button/header-button.stories.ts b/src/scss/components/header-button/header-button.stories.ts new file mode 100644 index 0000000..df1b34b --- /dev/null +++ b/src/scss/components/header-button/header-button.stories.ts @@ -0,0 +1,35 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import iconInfoUrl from "../../../assets/svg/icon-info.svg"; +import iconSearchUrl from "../../../assets/svg/icon-search.svg"; + +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Header/Button", + parameters: { + backgrounds: { + default: "dark", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Info: Story = { + render: () => html` + + Help docs + + + `, +}; + +export const Search: Story = { + render: () => html` + + Search + + + `, +}; diff --git a/src/scss/components/menu-toggle/_menu-toggle.scss b/src/scss/components/menu-toggle/_menu-toggle.scss new file mode 100644 index 0000000..46184a5 --- /dev/null +++ b/src/scss/components/menu-toggle/_menu-toggle.scss @@ -0,0 +1,36 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; +@use "../../tokens/spacing" as *; +@use "../../tokens/screens" as *; + +.iati-menu-toggle { + color: #fff; + border: none; + background-color: transparent; + font-size: 0.9rem; + font-family: $font-stack-heading; + font-weight: 600; + flex-direction: column; + display: inline-flex; + align-items: flex-end; + gap: 0.5em; + padding: 0; + align-self: stretch; + background-repeat: no-repeat; + background-size: contain; + justify-content: flex-end; + min-height: 2.2rem; + &--open { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='white' viewBox='0 0 38 17'%3E%3Cpath d='M0 0h38v3H0ZM0 7h38v3H0ZM0 14h38v3H0Z'/%3E%3C/svg%3E"); + } + &--close { + height: 3.5rem; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 26 26'%3E%3Cpath stroke='white' stroke-miterlimit='10' stroke-width='1.3' d='M13 25c6.6274 0 12-5.3726 12-12 0-6.62742-5.3726-12-12-12C6.37258 1 1 6.37258 1 13c0 6.6274 5.37258 12 12 12ZM18.4163 18.416 7.58386 7.58386M7.58386 18.416 18.4163 7.58386'/%3E%3C/svg%3E"); + } +} + +.iati-menu-toggle__label { + line-height: 1; + position: relative; + top: 0.25em; +} diff --git a/src/scss/components/menu-toggle/menu-toggle.stories.ts b/src/scss/components/menu-toggle/menu-toggle.stories.ts new file mode 100644 index 0000000..036bd18 --- /dev/null +++ b/src/scss/components/menu-toggle/menu-toggle.stories.ts @@ -0,0 +1,34 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Menu Toggle", + parameters: { + backgrounds: { + default: "dark", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Open: Story = { + render: () => html` + + `, +}; + +export const Close: Story = { + render: () => html` + + `, +}; diff --git a/src/scss/components/message/_message.scss b/src/scss/components/message/_message.scss new file mode 100644 index 0000000..0bbdc56 --- /dev/null +++ b/src/scss/components/message/_message.scss @@ -0,0 +1,27 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; + +.iati-message { + padding: 0.8em 1em; + font-family: $font-stack-heading; + font-size: 1.1rem; + line-height: 1; + margin: 0; + font-weight: 400; + a { + color: currentColor; + text-decoration-color: currentColor; + } +} + +.iati-message-highlight { + font-weight: 600; +} + +.iati-message--notice { + background-color: $color-grey-10; +} + +.iati-message--info { + background-color: $color-green-10; +} diff --git a/src/scss/components/message/message.stories.ts b/src/scss/components/message/message.stories.ts new file mode 100644 index 0000000..294e994 --- /dev/null +++ b/src/scss/components/message/message.stories.ts @@ -0,0 +1,32 @@ +import { html } from "lit"; + +import type { Meta, StoryObj } from "@storybook/web-components"; + +const meta: Meta = { + title: "Components/Message", +}; + +export default meta; +type Story = StoryObj; + +export const Notice: Story = { + render: () => html` +

+ You are viewing + VERSION 2.03 of IATI Standard Reference. View another version. +

+ `, +}; + +export const Info: Story = { + render: () => html` +

+ You are viewing + VERSION 2.03 of IATI Standard Reference. View another version. +

+ `, +}; diff --git a/src/scss/components/mobile-nav/_mobile-nav.scss b/src/scss/components/mobile-nav/_mobile-nav.scss new file mode 100644 index 0000000..2420d4a --- /dev/null +++ b/src/scss/components/mobile-nav/_mobile-nav.scss @@ -0,0 +1,89 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; +@use "../../tokens/spacing" as *; + +.iati-mobile-nav { + overflow: hidden; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: -1; + transition: all 0.5s; + &--open { + z-index: 100; + } +} + +.iati-mobile-nav__overlay { + height: 100%; + min-height: 100vh; + width: 100%; + background-color: #000; + opacity: 0; + position: absolute; + top: 0; + left: 0; + visibility: hidden; + transition: all 0.5s; + z-index: -1; + .iati-mobile-nav--open & { + opacity: 0.6; + visibility: visible; + } +} + +.iati-mobile-nav__menu { + background-color: $color-teal-90; + padding: 1rem; + color: #fff; + position: absolute; + z-index: 101; + right: 0; + top: 0; + height: 100%; + width: min(90vw, 400px); + margin: 0; + transform: translateX(400px); + transition: transform 0.5s; + .iati-mobile-nav--open & { + transform: translateX(0); + } + ul { + list-style-type: none; + margin: 0; + padding: 0; + } +} + +.iati-mobile-nav__label { + font-size: 1.375rem; + margin-block-end: 2rem; + margin: 0; +} + +.iati-mobile-nav__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-block-end: 2rem; +} + +.iati-mobile-nav__item { + border-block-start: 1px solid #fff; +} + +.iati-mobile-nav__link { + color: #fff; + text-decoration: none; + font-weight: 800; + font-size: 1.0625rem; + line-height: 1; + padding-block: 0.75em; + display: block; + transition: all 0.2s ease-in-out; + &:hover { + color: $color-blue-30; + } +} diff --git a/src/scss/components/mobile-nav/mobile-nav.stories.ts b/src/scss/components/mobile-nav/mobile-nav.stories.ts new file mode 100644 index 0000000..74146d6 --- /dev/null +++ b/src/scss/components/mobile-nav/mobile-nav.stories.ts @@ -0,0 +1,59 @@ +import { html } from "lit"; + +import type { Meta, StoryObj } from "@storybook/web-components"; +import { Close as MenuToggle } from "../menu-toggle/menu-toggle.stories"; + +const toolItems = [ + "Tool Home", + "About", + "Data Dashboards", + "Custom Data Download", +]; +const generalItems = ["IATI home", "News", "Events", "Contact", "Help docs"]; + +const meta: Meta = { + title: "Components/Mobile Nav", + argTypes: { + open: { + defaultValue: true, + control: { type: "boolean" }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const MobileNav: Story = { + render: (args) => html` +
+
+ +
+ `, +}; diff --git a/src/scss/components/newsletter-form/_newsletter-form.scss b/src/scss/components/newsletter-form/_newsletter-form.scss new file mode 100644 index 0000000..d69a33b --- /dev/null +++ b/src/scss/components/newsletter-form/_newsletter-form.scss @@ -0,0 +1,44 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; +@use "../../base/mixins"; +@use "../../tokens/screens" as *; + +.iati-newsletter-form { + color: #fff; + display: grid; + gap: 1rem; + min-width: min(31.25rem, calc(100vw - 2rem)); + @media (min-width: $screen-md) { + grid-template-columns: max-content 1fr; + } +} + +.iati-newsletter-form__item { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / -1; + align-items: end; + gap: 0.2rem; + @media (min-width: $screen-md) { + gap: 1rem; + } +} + +.iati-newsletter-form__label { + font-weight: 600; + grid-column: 1; + @media (min-width: $screen-md) { + text-align: end; + } +} + +.iati-newsletter-form__input { + max-width: 100%; + border: none; + background-color: #fff; + padding: 0.5em 1em; +} + +.iati-newsletter-form__submit { + grid-column: -1 / span 1; +} diff --git a/src/scss/components/newsletter-form/newsletter-form.stories.ts b/src/scss/components/newsletter-form/newsletter-form.stories.ts new file mode 100644 index 0000000..365c7aa --- /dev/null +++ b/src/scss/components/newsletter-form/newsletter-form.stories.ts @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Newsletter Form", + parameters: { + backgrounds: { + default: "dark", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => html` +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ `, +}; diff --git a/src/scss/components/piped-list/_piped-list.scss b/src/scss/components/piped-list/_piped-list.scss new file mode 100644 index 0000000..c9eab41 --- /dev/null +++ b/src/scss/components/piped-list/_piped-list.scss @@ -0,0 +1,28 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; + +.iati-piped-list { + display: flex; + flex-wrap: wrap; + gap: 0.5em; + list-style-type: none; + padding: 0; + margin: 0; + & > * { + line-height: 1; + position: relative; + &:not(:last-child) { + &:after { + content: ""; + position: absolute; + right: 1rem; + height: 120%; + width: 1px; + display: block; + background-color: currentColor; + top: -10%; + } + padding-inline-end: 2rem; + } + } +} diff --git a/src/scss/components/piped-list/piped-list.stories.ts b/src/scss/components/piped-list/piped-list.stories.ts new file mode 100644 index 0000000..7c3308d --- /dev/null +++ b/src/scss/components/piped-list/piped-list.stories.ts @@ -0,0 +1,20 @@ +import { html } from "lit"; + +import type { Meta, StoryObj } from "@storybook/web-components"; + +const items = ["IATI home", "News", "Events", "Contacts"]; + +const meta: Meta = { + title: "Components/Piped List", +}; + +export default meta; +type Story = StoryObj; + +export const PipedList: Story = { + render: () => html` + + `, +}; diff --git a/src/scss/components/search-bar/_search-bar.scss b/src/scss/components/search-bar/_search-bar.scss index f8ebc0f..6427e25 100644 --- a/src/scss/components/search-bar/_search-bar.scss +++ b/src/scss/components/search-bar/_search-bar.scss @@ -9,7 +9,7 @@ flex-grow: 1; padding: $padding-block; border: 1px solid $color-grey-20; - border-right: none; + border-inline-end: none; &::placeholder { color: $color-grey-40; diff --git a/src/scss/components/title-bar/_title-bar.scss b/src/scss/components/title-bar/_title-bar.scss new file mode 100644 index 0000000..90eaef3 --- /dev/null +++ b/src/scss/components/title-bar/_title-bar.scss @@ -0,0 +1,39 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; +@use "../../tokens/spacing" as *; +@use "../../tokens/screens" as *; + +.iati-header-title { + display: flex; + flex-direction: column; + gap: 0.8rem; + padding-inline-start: 1rem; + border-inline-start: $border-width solid $color-blue-50; + & > * { + margin: 0; + } +} + +.iati-header-title__eyebrow, +.iati-header-title__heading { + color: white; + font-family: $font-stack-heading; + font-weight: 800; +} + +.iati-header-title__eyebrow { + font-size: 1.1rem; + line-height: 1; + @media (min-width: $screen-sm) { + font-size: 1.5rem; + line-height: 1.2; + } +} + +.iati-header-title__heading { + font-size: 1.625rem; + line-height: 1; + @media (min-width: $screen-sm) { + font-size: 2.5rem; + } +} diff --git a/src/scss/components/title-bar/title-bar.stories.ts b/src/scss/components/title-bar/title-bar.stories.ts new file mode 100644 index 0000000..792c33f --- /dev/null +++ b/src/scss/components/title-bar/title-bar.stories.ts @@ -0,0 +1,23 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Title Bar", + parameters: { + backgrounds: { + default: "dark", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => html` +
+

IATI Tools

+

Country Development Finance Data

+
+ `, +}; diff --git a/src/scss/components/tool-nav/_tool-nav.scss b/src/scss/components/tool-nav/_tool-nav.scss new file mode 100644 index 0000000..ca08698 --- /dev/null +++ b/src/scss/components/tool-nav/_tool-nav.scss @@ -0,0 +1,26 @@ +@use "../../tokens/color" as *; +@use "../../tokens/font" as *; +@use "../../tokens/spacing" as *; + +.iati-tool-nav { + list-style-type: none; + display: flex; + padding: 0; + margin: 0; +} + +.iati-tool-nav-link { + text-decoration: none; + padding: 1rem; + font-family: $font-stack-heading; + font-weight: 600; + color: #fff; + font-size: 1.0625rem; + line-height: 1.1; + transition: all 0.2s ease-in-out; + display: inline-block; + &:hover { + background-color: $color-blue-30; + color: $color-teal-90; + } +} diff --git a/src/scss/components/tool-nav/tool-nav.stories.ts b/src/scss/components/tool-nav/tool-nav.stories.ts new file mode 100644 index 0000000..efab64d --- /dev/null +++ b/src/scss/components/tool-nav/tool-nav.stories.ts @@ -0,0 +1,29 @@ +import { html } from "lit"; + +import type { Meta, StoryObj } from "@storybook/web-components"; + +const items = ["Tool Home", "About", "Data Dashboards", "Custom Data Download"]; + +const meta: Meta = { + title: "Components/Header/Tool Nav", + parameters: { + backgrounds: { + default: "dark", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const ToolNav: Story = { + render: () => html` + + `, +}; diff --git a/src/scss/layout/_index.scss b/src/scss/layout/_index.scss index 440ce9a..f127e09 100644 --- a/src/scss/layout/_index.scss +++ b/src/scss/layout/_index.scss @@ -1,4 +1,3 @@ @forward "footer/footer"; @forward "header/header"; -@forward "title-bar/title-bar"; @forward "page/page"; diff --git a/src/scss/layout/footer/_footer.scss b/src/scss/layout/footer/_footer.scss index d0f61dc..b939a26 100644 --- a/src/scss/layout/footer/_footer.scss +++ b/src/scss/layout/footer/_footer.scss @@ -2,47 +2,63 @@ @use "../../tokens/font" as *; @use "../../tokens/spacing" as *; @use "../../base/mixins"; +@use "../../tokens/screens" as *; .iati-footer { - background-color: $color-teal-90; - color: white; - - & a { - color: currentColor; - } + color: #fff; + padding-block: 2rem; +} - & > div { - @include mixins.page-width-container(); - @include mixins.columns(); - flex-wrap: wrap; +.iati-footer__section { + &:not(:first-of-type) { + margin-block-start: 2rem; + padding-block-start: 2rem; + border-block-start: 1px solid $color-blue-30; } +} - hr { - border: none; - border-top: 1px solid $color-teal-70; - margin: 0; +.iati-footer__section--first .iati-footer__container { + display: flex; + gap: 2rem; + flex-direction: column; + @media (min-width: $screen-md) { + justify-content: space-between; + flex-direction: row; } +} - &__logo { - max-height: 80px; - max-width: 100%; - margin: $padding-block 0; +.iati-footer__section--last .iati-footer__container { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 2rem; + > :first-child { + margin-inline-end: auto; } +} - &__list { - &-title { - text-transform: uppercase; - font-family: $font-stack-heading; - font-weight: $font-weight-heading; - line-height: $line-height-body; - } +.iati-footer__container { + @include mixins.page-width-container(); +} - ul { - @include mixins.unstyled-list(); +.iati-footer__legal-nav { + color: currentColor; + font-weight: 600; + font-size: 1.0625rem; + line-height: 1.1; + a { + font-size: 1.0625rem; + line-height: 1.1; + color: currentColor; + text-decoration: none; + &:visited, + &:hover { + color: $color-blue-30; } } +} - &__social-icons { - @include mixins.columns(); - } +.iati-footer__social { + display: flex; + gap: 0.5rem; } diff --git a/src/scss/layout/footer/footer.stories.ts b/src/scss/layout/footer/footer.stories.ts index 7202c1b..26f14cc 100644 --- a/src/scss/layout/footer/footer.stories.ts +++ b/src/scss/layout/footer/footer.stories.ts @@ -1,10 +1,25 @@ import type { Meta, StoryObj } from "@storybook/web-components"; import { html } from "lit"; +import { CountrySwitcher } from "../../components/country-switcher/country-switcher.stories"; +import { + AdditionInfo as AdditionalInfoFooterBlock, + UsefulLinks as UsefulLinksFooterBlock, +} from "../../components/footer-block/footer-block.stories"; import { Linkedin, Twitter, Youtube } from "../../components/icon/icon.stories"; +import { Default as NewsletterForm } from "../../components/newsletter-form/newsletter-form.stories"; + +const legalNavItems = [ + html`Privacy`, + html`Data removal`, + html`© Copyright IATI 2024. All rights reserved`, +]; const meta: Meta = { title: "Layout/Footer", + parameters: { + layout: "fullscreen", + }, }; export default meta; @@ -12,42 +27,42 @@ type Story = StoryObj; export const Footer: Story = { render: (args) => html` -