diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2edeafb --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/.storybook/DocsTemplate.mdx b/.storybook/DocsTemplate.mdx new file mode 100644 index 0000000..0066ca2 --- /dev/null +++ b/.storybook/DocsTemplate.mdx @@ -0,0 +1,9 @@ +import { Meta, Title, Subtitle, Stories } from "@storybook/blocks"; + + + + + +<Subtitle /> + +<Stories /> diff --git a/.storybook/StoriesOnlyTemplate.mdx b/.storybook/StoriesOnlyTemplate.mdx deleted file mode 100644 index f5eec8b..0000000 --- a/.storybook/StoriesOnlyTemplate.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import { Meta, Title, Primary, Controls, Stories } from "@storybook/blocks"; - -<Meta isTemplate /> - -<Title /> - -<Stories /> diff --git a/.storybook/main.ts b/.storybook/main.ts index 1ebc717..730dd9d 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -5,6 +5,9 @@ const config: StorybookConfig = { framework: "@storybook/web-components-vite", stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], addons: ["@storybook/addon-essentials"], + docs: { + autodocs: true, + }, async viteFinal(config) { const { mergeConfig } = await import("vite"); return mergeConfig(config, { diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 541bbab..8db52e1 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,15 +1,17 @@ import type { Preview } from "@storybook/web-components"; import "../src/scss/iati.scss"; +import DocsTemplate from "./DocsTemplate.mdx"; const preview: Preview = { parameters: { options: { storySort: { - order: ["Core", "Components"], + order: ["Get Started", "Core", "Components", "Layout"], }, }, docs: { + page: DocsTemplate, source: { format: "dedent", }, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 013d63b..f82015b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,4 +22,9 @@ BREAKING CHANGE: removes the '.subcomponent' class. ### What is a breaking change? -A breaking change is any "incompatible API change". In the context of this design system, the API consists of CSS classes and SASS variables/functions/mixins etc. Any change which removes or renames a part of the API must be labelled a breaking change. +A breaking change is any "incompatible API change". In the context of this design system, the API consists of CSS classes and SASS variables/functions/mixins etc. + +Breaking changes include the following: + +- Any change which removes or renames a part of the API must be labelled a breaking change. +- Any change which would require a downstream consumer to change their HTML. diff --git a/README.md b/README.md index 31b4ef1..e0677fc 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,81 @@ # IATI Design System -## Usage +## What is the IATI Design System? -### NPM +The IATI Design System is a set of reusable styles and components which should be used in IATI web products, along with guidance on how to use them. -``` -npm install iati-design-system -``` +## How do I use the IATI Design System? -### CDN +### CDN (Recommended) -``` -https://cdn.jsdelivr.net/npm/iati-design-system@[X.Y.Z]/dist/css/iati.css +The recommended way to use the design system in an IATI product is by including the CSS from the CDN, using the url below, replacing `<VERSION>` with the version you would like to use: + +```code +https://cdn.jsdelivr.net/npm/iati-design-system@<VERSION>/dist/css/iati.min.css ``` -## Development +This project is versioned using [Semantic Versioning](https://semver.org/). Versions can be specified as a major, minor, or patch version e.g. `1`, `1.2`, or `1.2.3`. The latest version is shown on the [GitHub releases page](https://github.com/IATI/design-system/releases). We recommend fixing to a specific patch version so that upgrades can be checked explicitly before deployment, but as a minimum you should fix to a major version to prevent unexpected breaking changes. -Start storybook: +To include the CSS in a HTML project, add the following code inside the `<head>` of your HTML pages: +```html +<link + href="https://cdn.jsdelivr.net/npm/iati-design-system@<VERSION>/dist/css/iati.min.css" + rel="stylesheet" +/> ``` -npm start + +Once this link is included, core styles will be applied, and all component and layout CSS classes will be available to apply to HTML elements, for example the `.iati-button` class: + +```html +<button class="iati-button">Button</button> ``` -## Production build +### NPM (Optional) -Build CSS: +It is also possible to include the design system in a [Sass](https://sass-lang.com/) project. -``` -npm run build +First install with npm: + +```code +npm install iati-design-system ``` -Build storybook: +Then import the package in your `.scss` file. The [Node.js Package Importer](https://sass-lang.com/documentation/at-rules/use/#node-js-package-importer) is required to use this syntax. +```css +@use "pkg:iati-design-system"; ``` -npm run build:storybook -``` + +## How do I contribute to the IATI Design System? + +### Prerequisites + +#### Node v20 + +IATI Design System requires Node v20. We recommend installing [Node Version Manager](https://github.com/nvm-sh/nvm) and running the command `nvm use` to use the Node version set in the `.nvmrc` file at the root of the project. + +### Running locally + +To run Storybook locally, take the following steps: + +1. Install dependencies: `npm install` +2. Start Storybook: `npm start` + +You will see live updates in the browser when you update styles or stories. + +### Production build + +#### CSS + +To check the CSS production build, take the following steps: + +1. Build the CSS: `npm run build` +2. View the CSS file: `./dist/css/iati.css`. + +#### Storybook + +The check the Storybook production build, take the following steps: + +1. Build the Storybook: `npm run build:storybook` +2. Serve the Storybook: `npm run serve:storybook` diff --git a/package.json b/package.json index 2cdf49e..6275b64 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "start": "storybook dev -p 6006", "build": "sass --pkg-importer=node --no-source-map --style=expanded src/scss:dist/css", "build:storybook": "storybook build", + "serve:storybook": "npx http-server -o ./storybook-static", "lint": "npm run lint:eslint && npm run lint:prettier", "lint:eslint": "eslint", "lint:prettier": "prettier . --check", diff --git a/src/scss/base/_index.scss b/src/scss/base/_index.scss index d70ed04..85802a3 100644 --- a/src/scss/base/_index.scss +++ b/src/scss/base/_index.scss @@ -1,2 +1,3 @@ @forward "normalize"; @forward "reset"; +@forward "mixins"; diff --git a/src/scss/base/_mixins.scss b/src/scss/base/_mixins.scss new file mode 100644 index 0000000..3b0ce85 --- /dev/null +++ b/src/scss/base/_mixins.scss @@ -0,0 +1,25 @@ +@use "../tokens/color" as *; +@use "../tokens/spacing" as *; + +@mixin page-width-container { + width: 100%; + max-width: $page-width-max; + margin-left: auto; + margin-right: auto; + padding: $padding-block; +} + +@mixin columns { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + align-items: center; + column-gap: $padding-block; + row-gap: $padding-block; +} + +@mixin unstyled-list { + list-style-type: none; + margin: 0; + padding: 0; +} diff --git a/src/scss/base/_reset.scss b/src/scss/base/_reset.scss index 08f0941..390d2eb 100644 --- a/src/scss/base/_reset.scss +++ b/src/scss/base/_reset.scss @@ -16,3 +16,18 @@ strong { em { font-style: italic; } + +html { + height: 100%; +} + +body { + height: 100%; + display: flex; + flex-flow: column; +} + +a:has(i), +a:has(img) { + text-decoration: none; +} diff --git a/src/scss/components/_index.scss b/src/scss/components/_index.scss index 5ccec41..fbea8e9 100644 --- a/src/scss/components/_index.scss +++ b/src/scss/components/_index.scss @@ -1 +1,6 @@ +@forward "button/button"; @forward "callout/callout"; +@forward "card/card"; +@forward "icon/icon"; +@forward "search-bar/search-bar"; +@forward "table/table"; diff --git a/src/scss/components/button/_button.scss b/src/scss/components/button/_button.scss new file mode 100644 index 0000000..f084647 --- /dev/null +++ b/src/scss/components/button/_button.scss @@ -0,0 +1,14 @@ +@use "../../tokens/color" as *; +@use "../../tokens/spacing" as *; + +.iati-button { + background-color: $color-teal-90; + border: none; + color: white; + text-transform: uppercase; + padding: $padding-block; + + &:hover { + background-color: $color-teal-80; + } +} diff --git a/src/scss/components/button/button.stories.ts b/src/scss/components/button/button.stories.ts new file mode 100644 index 0000000..f2d362a --- /dev/null +++ b/src/scss/components/button/button.stories.ts @@ -0,0 +1,14 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Button", +}; + +export default meta; +type Story = StoryObj; + +export const Button: Story = { + render: () => html`<button class="iati-button">Button</button>`, +}; diff --git a/src/scss/components/callout/_callout.scss b/src/scss/components/callout/_callout.scss index 0f60598..9948d42 100644 --- a/src/scss/components/callout/_callout.scss +++ b/src/scss/components/callout/_callout.scss @@ -1,4 +1,5 @@ @use "../../tokens/color" as *; +@use "../../tokens/font" as *; @use "../../tokens/spacing" as *; .iati-callout { @@ -39,6 +40,6 @@ &__title { font-weight: bold; font-size: 1.2rem; - line-height: 2; + line-height: $line-height-body; } } diff --git a/src/scss/components/callout/callout.stories.ts b/src/scss/components/callout/callout.stories.ts index 429f418..abbb26e 100644 --- a/src/scss/components/callout/callout.stories.ts +++ b/src/scss/components/callout/callout.stories.ts @@ -1,16 +1,8 @@ import type { Meta, StoryObj } from "@storybook/web-components"; import { html } from "lit"; -import Template from "../../../../.storybook/StoriesOnlyTemplate.mdx"; - const meta: Meta = { title: "Components/Callout", - tags: ["autodocs"], - parameters: { - docs: { - page: Template, - }, - }, }; export default meta; diff --git a/src/scss/components/card/_card.scss b/src/scss/components/card/_card.scss new file mode 100644 index 0000000..a93aa4e --- /dev/null +++ b/src/scss/components/card/_card.scss @@ -0,0 +1,31 @@ +@use "../../tokens/color" as *; +@use "../../tokens/spacing" as *; + +.iati-card { + box-shadow: 0 4px 5px $color-grey-20; + background-color: white; + border-top: $border-width solid $color-teal-90; + padding: $padding-block; + + & :first-child { + margin-top: 0; + } + + & :last-child { + margin-bottom: 0; + } + + &__title { + margin: 0; + font-weight: bold; + font-size: 1.2rem; + } + + &__subtitle { + color: $color-teal-90; + font-size: 0.8rem; + font-weight: bold; + margin: 0; + text-transform: uppercase; + } +} diff --git a/src/scss/components/card/card.stories.ts b/src/scss/components/card/card.stories.ts new file mode 100644 index 0000000..7d3d2c3 --- /dev/null +++ b/src/scss/components/card/card.stories.ts @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Card", +}; + +export default meta; +type Story = StoryObj; + +export const Card: Story = { + render: () => html` + <div class="iati-card"> + <p class="iati-card__title">Card Title</p> + <p class="iati-card__subtitle">Subtitle</p> + <p>Card content.</p> + </div> + `, +}; diff --git a/src/scss/components/icon/_icon.scss b/src/scss/components/icon/_icon.scss new file mode 100644 index 0000000..0f45b7e --- /dev/null +++ b/src/scss/components/icon/_icon.scss @@ -0,0 +1,22 @@ +.iati-icon { + display: inline-block; + height: 45px; + width: 45px; + border-radius: 100%; + background-color: white; + background-repeat: no-repeat; + background-size: 60%; + background-position: center; + + &--youtube { + background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%23FF0000' viewBox='0 0 16 16'><path d='M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 0 1 1.415 1.42c.101.38.172.883.22 1.402l.01.104.022.26.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105-.009.104c-.05.572-.124 1.14-.235 1.558a2.01 2.01 0 0 1-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006-.087-.004-.171-.007-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.01 2.01 0 0 1-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31 31 0 0 1 0 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103.003-.052.008-.104.022-.26.01-.104c.048-.519.119-1.023.22-1.402a2.01 2.01 0 0 1 1.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007.172-.006.086-.003.171-.007A100 100 0 0 1 7.858 2zM6.4 5.209v4.818l4.157-2.408z'/></svg>"); + } + + &--twitter { + background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%231DA1F2' viewBox='0 0 16 16'><path d='M5.026 15c6.038 0 9.341-5.003 9.341-9.334q.002-.211-.006-.422A6.7 6.7 0 0 0 16 3.542a6.7 6.7 0 0 1-1.889.518 3.3 3.3 0 0 0 1.447-1.817 6.5 6.5 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.32 9.32 0 0 1-6.767-3.429 3.29 3.29 0 0 0 1.018 4.382A3.3 3.3 0 0 1 .64 6.575v.045a3.29 3.29 0 0 0 2.632 3.218 3.2 3.2 0 0 1-.865.115 3 3 0 0 1-.614-.057 3.28 3.28 0 0 0 3.067 2.277A6.6 6.6 0 0 1 .78 13.58a6 6 0 0 1-.78-.045A9.34 9.34 0 0 0 5.026 15'/></svg>"); + } + + &--linkedin { + background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='%230762C8' viewBox='0 0 16 16'><path d='M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248S2.4 3.226 2.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016l.016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z'/></svg>"); + } +} diff --git a/src/scss/components/icon/icon.stories.ts b/src/scss/components/icon/icon.stories.ts new file mode 100644 index 0000000..28813b3 --- /dev/null +++ b/src/scss/components/icon/icon.stories.ts @@ -0,0 +1,21 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Icon", +}; + +export default meta; +type Story = StoryObj; + +export const Youtube: Story = { + render: () => html`<i class="iati-icon iati-icon--youtube"></i>`, +}; + +export const Twitter: Story = { + render: () => html`<i class="iati-icon iati-icon--twitter"></i>`, +}; + +export const Linkedin: Story = { + render: () => html`<i class="iati-icon iati-icon--linkedin"></i>`, +}; diff --git a/src/scss/components/search-bar/_search-bar.scss b/src/scss/components/search-bar/_search-bar.scss new file mode 100644 index 0000000..f8ebc0f --- /dev/null +++ b/src/scss/components/search-bar/_search-bar.scss @@ -0,0 +1,23 @@ +@use "../../tokens/color" as *; +@use "../../tokens/spacing" as *; + +.iati-search-bar { + display: flex; + flex-flow: row nowrap; + + & input[type="text"] { + flex-grow: 1; + padding: $padding-block; + border: 1px solid $color-grey-20; + border-right: none; + + &::placeholder { + color: $color-grey-40; + } + + &:focus { + outline: none; + border-color: $color-grey-30; + } + } +} diff --git a/src/scss/components/search-bar/search-bar.stories.ts b/src/scss/components/search-bar/search-bar.stories.ts new file mode 100644 index 0000000..6be711d --- /dev/null +++ b/src/scss/components/search-bar/search-bar.stories.ts @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Search Bar", +}; + +export default meta; +type Story = StoryObj; + +export const Search: Story = { + render: () => html` + <div class="iati-search-bar"> + <input type="text" placeholder="Search" /> + <button type="submit" class="iati-button">Go</button> + </div> + `, +}; diff --git a/src/scss/components/table/_table.scss b/src/scss/components/table/_table.scss new file mode 100644 index 0000000..a319a8a --- /dev/null +++ b/src/scss/components/table/_table.scss @@ -0,0 +1,29 @@ +@use "../../tokens/color" as *; +@use "../../tokens/spacing" as *; + +table { + border-collapse: collapse; + display: inline-block; + max-width: 100%; + overflow: auto; + + thead, + th { + background-color: $color-grey-10; + + &, + p { + font-weight: bold; + } + } + + th, + td { + border: 1px solid $color-grey-20; + padding: 0.5rem; + } + + caption { + margin: $padding-block 0; + } +} diff --git a/src/scss/components/table/table.stories.ts b/src/scss/components/table/table.stories.ts new file mode 100644 index 0000000..c6fb659 --- /dev/null +++ b/src/scss/components/table/table.stories.ts @@ -0,0 +1,56 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +const meta: Meta = { + title: "Components/Table", +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => html` + <table> + <tr> + <th>Column 1</th> + <th>Column 2</th> + <th>Column 3</th> + <th>Column 4</th> + <th>Column 5</th> + </tr> + <tr> + <td>Cell 1</td> + <td>Cell 2</td> + <td>Cell 3</td> + <td>Cell 4</td> + <td>Cell 5</td> + </tr> + </table> + `, +}; + +export const Scrolling: Story = { + render: () => html` + <div style="width: 300px;"> + <table> + <caption> + This table should scroll within its fixed-width container. + </caption> + <tr> + <th>Column 1</th> + <th>Column 2</th> + <th>Column 3</th> + <th>Column 4</th> + <th>Column 5</th> + </tr> + <tr> + <td>Cell 1</td> + <td>Cell 2</td> + <td>Cell 3</td> + <td>Cell 4</td> + <td>Cell 5</td> + </tr> + </table> + </div> + `, +}; diff --git a/src/scss/iati.scss b/src/scss/iati.scss index 326dea4..970df09 100644 --- a/src/scss/iati.scss +++ b/src/scss/iati.scss @@ -2,6 +2,7 @@ @use "tokens"; @use "typography"; @use "components"; +@use "layout"; // Expose tokens for use in downstream applications @forward "tokens"; diff --git a/src/scss/layout/_index.scss b/src/scss/layout/_index.scss new file mode 100644 index 0000000..440ce9a --- /dev/null +++ b/src/scss/layout/_index.scss @@ -0,0 +1,4 @@ +@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 new file mode 100644 index 0000000..2fa0c9d --- /dev/null +++ b/src/scss/layout/footer/_footer.scss @@ -0,0 +1,45 @@ +@use "../../tokens/color" as *; +@use "../../tokens/spacing" as *; +@use "../../base/mixins"; + +.iati-footer { + background-color: $color-teal-90; + color: white; + + & a { + color: currentColor; + } + + & > div { + @include mixins.page-width-container(); + @include mixins.columns(); + flex-wrap: wrap; + } + + hr { + border: none; + border-top: 1px solid $color-teal-70; + margin: 0; + } + + &__logo { + max-height: 80px; + max-width: 100%; + margin: $padding-block 0; + } + + &__list { + &-title { + text-transform: uppercase; + font-weight: bold; + } + + ul { + @include mixins.unstyled-list(); + } + } + + &__social-icons { + @include mixins.columns(); + } +} diff --git a/src/scss/layout/footer/footer.stories.ts b/src/scss/layout/footer/footer.stories.ts new file mode 100644 index 0000000..aff691f --- /dev/null +++ b/src/scss/layout/footer/footer.stories.ts @@ -0,0 +1,54 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +import { Linkedin, Twitter, Youtube } from "../../components/icon/icon.stories"; + +const meta: Meta = { + title: "Layout/Footer", +}; + +export default meta; +type Story = StoryObj; + +export const Footer: Story = { + render: (args) => html` + <footer class="iati-footer"> + <div> + <a href="https://iatistandard.org/"> + <img + src="http://styles.iatistandard.org/assets/svg/source/logo-white.svg" + class="iati-footer__logo" + /> + </a> + <div class="iati-footer__list"> + <span class="iati-footer__list-title">Useful Links</span> + <ul> + <li><a>Contact</a></li> + <li><a>Privacy Policy</a></li> + </ul> + </div> + </div> + <hr /> + <div> + <span>© Copyright 2024 IATI Secretariat.</span> + <div class="iati-footer__social-icons"> + <a href="https://twitter.com/IATI_aid" aria-label="Twitter"> + ${Twitter.render?.call({ ...args })} + </a> + <a + href="https://www.youtube.com/channel/UCAVH1gcgJXElsj8ENC-bDQQ" + aria-label="YouTube" + > + ${Youtube.render?.call({ ...args })} + </a> + <a + href="https://www.linkedin.com/company/international-aid-transparency-initiative/" + aria-label="LinkedIn" + > + ${Linkedin.render?.call({ ...args })} + </a> + </div> + </div> + </footer> + `, +}; diff --git a/src/scss/layout/header/_header.scss b/src/scss/layout/header/_header.scss new file mode 100644 index 0000000..a62f0c8 --- /dev/null +++ b/src/scss/layout/header/_header.scss @@ -0,0 +1,34 @@ +@use "../../tokens/color" as *; +@use "../../tokens/spacing" as *; +@use "../../base/mixins"; + +.iati-header { + @include mixins.page-width-container(); + @include mixins.columns(); + background-color: $color-teal-90; + background-color: white; + flex-wrap: wrap; + + &__logo { + max-height: 60px; + max-width: 100%; + } + + &__nav { + @include mixins.unstyled-list(); + } + + &__nav-item { + display: inline-block; + font-size: 1.1rem; + + & a { + text-decoration: none; + padding: 0.5rem; + + &:hover { + border-bottom: 4px solid currentColor; + } + } + } +} diff --git a/src/scss/layout/header/header.stories.ts b/src/scss/layout/header/header.stories.ts new file mode 100644 index 0000000..8a2778a --- /dev/null +++ b/src/scss/layout/header/header.stories.ts @@ -0,0 +1,29 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +const meta: Meta = { + title: "Layout/Header", +}; + +export default meta; +type Story = StoryObj; + +export const Header: Story = { + render: () => html` + <header class="iati-header"> + <div> + <a href="https://iatistandard.org/"> + <img + src="http://styles.iatistandard.org/assets/svg/source/logo-colour.svg" + class="iati-header__logo" + /> + </a> + </div> + <ul class="iati-header__nav"> + <li class="iati-header__nav-item"><a>Events</a></li> + <li class="iati-header__nav-item"><a>Contact</a></li> + <li class="iati-header__nav-item"><a>Search</a></li> + </ul> + </header> + `, +}; diff --git a/src/scss/layout/page/_page.scss b/src/scss/layout/page/_page.scss new file mode 100644 index 0000000..8a86fb0 --- /dev/null +++ b/src/scss/layout/page/_page.scss @@ -0,0 +1,6 @@ +@use "../../base/mixins"; + +.iati-main { + @include mixins.page-width-container(); + flex: 1; +} diff --git a/src/scss/layout/page/page.stories.ts b/src/scss/layout/page/page.stories.ts new file mode 100644 index 0000000..23c1705 --- /dev/null +++ b/src/scss/layout/page/page.stories.ts @@ -0,0 +1,24 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; +import { html } from "lit"; + +import { Footer } from "../footer/footer.stories"; +import { Header } from "../header/header.stories"; +import { TitleBar } from "../title-bar/title-bar.stories"; + +const meta: Meta = { + title: "Layout/Page", +}; + +export default meta; +type Story = StoryObj; + +export const Page: Story = { + render: (args) => html` + ${Header.render?.call({ ...args })} ${TitleBar.render?.call({ ...args })} + <main class="iati-main"> + <h1>Page heading</h1> + <p>Page contents</p> + </main> + ${Footer.render?.call({ ...args })} + `, +}; diff --git a/src/scss/layout/title-bar/_title-bar.scss b/src/scss/layout/title-bar/_title-bar.scss new file mode 100644 index 0000000..005dcbc --- /dev/null +++ b/src/scss/layout/title-bar/_title-bar.scss @@ -0,0 +1,37 @@ +@use "../../tokens/color" as *; +@use "../../tokens/spacing" as *; +@use "../../base/mixins"; + +.iati-title-bar { + background-color: $color-teal-90; + + &__title { + @include mixins.page-width-container(); + color: white; + font-weight: bold; + font-size: 2.5rem; + padding: 1.2em $padding-block; + position: relative; + + &:before { + content: ""; + border-left: $border-width solid $color-teal-50; + padding: $padding-block; + } + + &:after { + content: ""; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + background-image: url("http://styles.iatistandard.org/assets/svg/source/marque-white.svg"); + background-repeat: no-repeat; + background-position: right center; + background-size: 300px; + opacity: 0.2; + pointer-events: none; + } + } +} diff --git a/src/scss/layout/title-bar/title-bar.stories.ts b/src/scss/layout/title-bar/title-bar.stories.ts new file mode 100644 index 0000000..8b5fa63 --- /dev/null +++ b/src/scss/layout/title-bar/title-bar.stories.ts @@ -0,0 +1,18 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { html } from "lit"; + +const meta: Meta = { + title: "Layout/Title Bar", +}; + +export default meta; +type Story = StoryObj; + +export const TitleBar: Story = { + render: () => html` + <div class="iati-title-bar"> + <div class="iati-title-bar__title">Title</div> + </div> + `, +}; diff --git a/src/scss/tokens/_font.scss b/src/scss/tokens/_font.scss index aaf4cde..4d9de69 100644 --- a/src/scss/tokens/_font.scss +++ b/src/scss/tokens/_font.scss @@ -1,3 +1,5 @@ $font-stack-heading: "Avenir", Helvetica, Arial, sans-serif; $font-stack-body: "Avenir", Helvetica, Arial, sans-serif; $font-stack-monospace: monospace; + +$line-height-body: 2; diff --git a/src/scss/tokens/_spacing.scss b/src/scss/tokens/_spacing.scss index e167172..11a9d57 100644 --- a/src/scss/tokens/_spacing.scss +++ b/src/scss/tokens/_spacing.scss @@ -1,3 +1,5 @@ $border-width: 4px; $padding-block: 1rem; + +$page-width-max: 1280px; diff --git a/src/scss/typography/_body.scss b/src/scss/typography/_body.scss index 7579477..dd2c8a1 100644 --- a/src/scss/typography/_body.scss +++ b/src/scss/typography/_body.scss @@ -7,17 +7,21 @@ html { color: $color-grey-90; } +body, p, a, li { - line-height: 2; + line-height: $line-height-body; font-weight: 300; } +li p { + margin: 0; +} + a { color: $color-teal-80; text-decoration: underline; - line-height: inherit; &:hover { text-decoration: none; @@ -29,3 +33,18 @@ blockquote { padding: 0 $padding-block; margin-left: $padding-block; } + +dl { + dt, + dd { + line-height: $line-height-body; + } + + dt { + font-weight: bold; + } + + dd { + margin-left: $padding-block; + } +} diff --git a/src/scss/typography/typography.stories.ts b/src/scss/typography/typography.stories.ts index c1cef18..1997132 100644 --- a/src/scss/typography/typography.stories.ts +++ b/src/scss/typography/typography.stories.ts @@ -1,16 +1,8 @@ import type { Meta, StoryObj } from "@storybook/web-components"; import { html } from "lit"; -import Template from "../../../.storybook/StoriesOnlyTemplate.mdx"; - const meta: Meta = { title: "Core/Typography", - tags: ["autodocs"], - parameters: { - docs: { - page: Template, - }, - }, }; export default meta; @@ -66,6 +58,9 @@ export const Lists: Story = { <ul> <li>This is an unordered list.</li> <li>It has bulleted items.</li> + <li> + <p>Paragraphs within list items shouldn't create extra margins.</p> + </li> </ul> <p>Below is an ordered list:</p> <ol> @@ -75,6 +70,15 @@ export const Lists: Story = { `, }; +export const DescriptionLists: Story = { + render: () => html` + <dl> + <dt>Term</dt> + <dd>Definition</dd> + </dl> + `, +}; + export const Quotes: Story = { render: () => html` <p> diff --git a/src/start.mdx b/src/start.mdx index 9ff0e1a..98d0887 100644 --- a/src/start.mdx +++ b/src/start.mdx @@ -1,36 +1,7 @@ -import { Meta, Title } from "@storybook/blocks"; +import { Markdown, Meta, Title } from "@storybook/blocks"; -<Meta title="Get Started" /> - -<Title>IATI Design System - -## Installation - -There are multiple ways to use the IATI Design System CSS in your product: - -### Option 1: CDN - -1. Add a link to the CSS in the `` section of your HTML page: +import ReadMe from "../README.md?raw"; - ```html - - - - ``` - -### Option 2: NPM - -1. Install the package with NPM: - - ``` - npm install iati-design-system - ``` - -2. The CSS can be imported from the following location: + - ```javascript - import "iati-design-system/dist/css/iati.css"; - ``` +{ReadMe}