diff --git a/.changeset/eager-spiders-stay.md b/.changeset/eager-spiders-stay.md new file mode 100644 index 0000000000..9dd2a05960 --- /dev/null +++ b/.changeset/eager-spiders-stay.md @@ -0,0 +1,4 @@ +--- +"@rhds/elements": patch +--- +``: improve compatibility with SSR diff --git a/.changeset/moody-cobras-happen.md b/.changeset/moody-cobras-happen.md new file mode 100644 index 0000000000..5480d4dd08 --- /dev/null +++ b/.changeset/moody-cobras-happen.md @@ -0,0 +1,4 @@ +--- +"@rhds/elements": patch +--- +``: fixed styles and typings for the `RhAlert.toast()` method diff --git a/.changeset/sixty-cases-like.md b/.changeset/sixty-cases-like.md new file mode 100644 index 0000000000..dcbddbad5d --- /dev/null +++ b/.changeset/sixty-cases-like.md @@ -0,0 +1,4 @@ +--- +"@rhds/elements": patch +--- +``: prevent error when hydrating server-side-rendered icons diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 1af8d750e5..0884a4d189 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '18' + node-version: '22' cache: npm - uses: google/wireit@setup-github-actions-caching/v2 diff --git a/.github/workflows/gzip.yml b/.github/workflows/gzip.yml index 9e50a3cf61..613c26aea1 100644 --- a/.github/workflows/gzip.yml +++ b/.github/workflows/gzip.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 18 + node-version: '22' cache: npm - uses: preactjs/compressed-size-action@v2 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8e4c5626b..65db709f45 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '18' + node-version: '22' cache: npm - uses: google/wireit@setup-github-actions-caching/v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 22f45c8f5e..ab7e7b47bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: - name: Configure node version uses: actions/setup-node@v3 with: - node-version: '18' + node-version: '22' cache: npm - uses: google/wireit@setup-github-actions-caching/v2 diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index 3bb682f067..1e7ea4e2f1 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' cache: 'npm' - run: npm i semantic-release @changesets/read --prefer-offline - uses: actions/github-script@v7 diff --git a/.gitignore b/.gitignore index 77450f1230..1898cef97b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,10 @@ node_modules _site docs/pfe.min* docs/assets/playgrounds/ +docs/assets/javascript/elements/*.js +!docs/assets/javascript/elements/uxdot-example.js +!docs/assets/javascript/elements/uxdot-installation-tabs.js +!docs/assets/javascript/elements/uxdot-pattern.js docs/_data/playgrounds.json # Build artifacts diff --git a/.nvmrc b/.nvmrc index 790e1105f2..751f4c9f38 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.10.0 +v22.7.0 diff --git a/.stylelintrc.yml b/.stylelintrc.yml index 9c603cc6ef..a03f0401fd 100644 --- a/.stylelintrc.yml +++ b/.stylelintrc.yml @@ -34,6 +34,8 @@ rules: ignoreTypes: - heading - actions + - rendered + - pending selector-pseudo-element-no-unknown: - true diff --git a/docs/_includes/layouts/base.njk b/docs/_includes/layouts/base.njk index cbcc15a596..fa131fd377 100644 --- a/docs/_includes/layouts/base.njk +++ b/docs/_includes/layouts/base.njk @@ -17,6 +17,7 @@ {% include "../partials/meta.html" %} + {# On browsers that don't yet support native declarative shadow DOM, a @@ -41,14 +42,10 @@ {% include "../partials/javascript/global.html" %} - {%- if hasToc %} - - - {%- endif %} - Skip to main content + Skip to main content {{ content | safe }} diff --git a/docs/_includes/layouts/pages/basic.njk b/docs/_includes/layouts/pages/basic.njk index c125f3596d..1157a4d2df 100755 --- a/docs/_includes/layouts/pages/basic.njk +++ b/docs/_includes/layouts/pages/basic.njk @@ -22,11 +22,9 @@ importElements: {% include 'partials/component/header.njk' %} {% if hasToc and (content | toc).length > 0 %} - + + {{ content | toc | safe }} + {% endif %}
diff --git a/docs/_includes/layouts/pages/elements.njk b/docs/_includes/layouts/pages/elements.njk index f725b6fdd1..22191a5759 100644 --- a/docs/_includes/layouts/pages/elements.njk +++ b/docs/_includes/layouts/pages/elements.njk @@ -15,11 +15,8 @@ eleventyComputed: - rh-tag - "{{ doc.tagName or element.tagName }}" --- -{%- if hasToc %} - -{%- endif %} {% if doc.pageTitle == 'Code' %} - + {% endif %} @@ -72,11 +69,9 @@ eleventyComputed: {% if hasToc and (content | toc).length > 0 %} - + + {{ content | toc | safe }} + {% endif %}
diff --git a/docs/_includes/layouts/pages/pattern.njk b/docs/_includes/layouts/pages/pattern.njk index fb61dac21f..f3e6e883eb 100755 --- a/docs/_includes/layouts/pages/pattern.njk +++ b/docs/_includes/layouts/pages/pattern.njk @@ -7,16 +7,10 @@ importElements: - rh-subnav - rh-code-block --- -{% if hasToc %} - -{% endif %} -{% if hasToc %} - -{% endif %} {% if tokenSearch %} @@ -29,11 +23,9 @@ importElements: {% include 'partials/component/header.njk' %} {% if hasToc and (content | toc).length > 0 %} - + + {{ content | toc | safe }} + {% endif %}
diff --git a/docs/_includes/partials/component/sidenav.njk b/docs/_includes/partials/component/sidenav.njk index a699e12051..a462b3abde 100644 --- a/docs/_includes/partials/component/sidenav.njk +++ b/docs/_includes/partials/component/sidenav.njk @@ -1,92 +1,65 @@ -
    - Home - {# - Loop through nav dropdowns - This uses data passed in via the eleventy.config.cjs file in the root - TODO: more this data to a global data, ideally this data would be normalized - prior to getting to the component template level. - #} - {%- for link in sideNavDropdowns -%} - {%- set active = link.url === page.url -%} - {%- set open = link.url in page.url -%} -
  • - -
    - {{ link.title }} - - {% if link.collection === "elementDocs" %} - {%- set active = '/elements/' === page.url -%} - All elements - {%- for tagName, docs in collections.elementDocs | groupby('tagName') -%} - {%- set fst = docs[0] -%} - {%- set slug = fst.slug -%} - {%- set href = '/elements/' + slug + '/' -%} - {%- set active = href === page.url -%} - {%- set name = (fst.alias) or (slug | deslugify) %} - {%- set planned = isPlanned(repoStatus, name) %} - {% if not (tagName in comingSoonItems) %} - - {{ (fst.alias) or (slug | deslugify) }}{% if planned %} Planned{% endif %} - - {% endif %} - {%- endfor -%} - {% else %} - {% if link.collection === 'token' %} - {%- set active = page.url === "/tokens/" %} - Overview - {% endif -%} + Home + {# + Loop through nav dropdowns + This uses data passed in via the eleventy.config.cjs file in the root + TODO: more this data to a global data, ideally this data would be normalized + prior to getting to the component template level. + #} + {%- for link in sideNavDropdowns -%} + {%- set active = link.url === page.url -%} + {%- set open = link.url in page.url -%} + +
    + {{ link.title }} + + {% if link.collection === "elementDocs" %} + {%- set active = '/elements/' === page.url -%} + All elements + {%- for tagName, docs in collections.elementDocs | groupby('tagName') -%} + {%- set fst = docs[0] -%} + {%- set slug = fst.slug -%} + {%- set href = '/elements/' + slug + '/' -%} + {%- set active = href === page.url -%} + {%- set name = (fst.alias) or (slug | deslugify) %} + {%- set planned = isPlanned(repoStatus, name) %} + {% if not (tagName in comingSoonItems) %} + + {{- fst.alias or (slug | deslugify) -}} + {%- if planned -%}Planned{%- endif -%} + + {% endif %} + {%- endfor -%} + {% else %} + {% if link.collection === 'token' %} + {%- set active = page.url === "/tokens/" %} + Overview + {% endif -%} - {%- if (link.collection !== 'elementDocs') and (link.collection !== 'token') -%} - {%- set sortedOn = "data.order" %} - {%- set sideNavCollection = collections[link.collection] | sort(attribute=sortedOn, case_sensitive=true) %} - {% else %} - {%- set sideNavCollection = collections[link.collection] %} - {%- endif -%} + {%- if (link.collection !== 'elementDocs') and (link.collection !== 'token') -%} + {%- set sortedOn = "data.order" %} + {%- set sideNavCollection = collections[link.collection] | sort(attribute=sortedOn, case_sensitive=true) %} + {% else %} + {%- set sideNavCollection = collections[link.collection] %} + {%- endif -%} - {%- for sublink in sideNavCollection -%} - {%- set active = sublink.url === page.url -%} - - {{- (sublink.data.sidenavTitle) - or (sublink.data.title or '' | capitalize) - or (sublink.title or ''| capitalize) -}} - - {%- endfor -%} - {% endif %} - -
    -
    -
  • - {% endfor %} + {%- for sublink in sideNavCollection -%} + {%- set active = sublink.url === page.url -%} + + {{- (sublink.data.sidenavTitle or sublink.data.title or sublink.title) | capitalize -}} + + {%- endfor -%} + {% endif %} + + + -
  • - - Design/code status - -
  • + {% endfor %} -
  • - - Release notes - -
  • - -
  • - - Get support - -
  • - - {%- if runMode == 'watch' -%} -
  • - - Cheat sheet! - -
  • - {%- endif -%} -
+ Design/code status + Release notes + Get support + {%- if runMode == 'watch' -%} + Cheat sheet! + {%- endif -%}
diff --git a/docs/_includes/partials/javascript/global.html b/docs/_includes/partials/javascript/global.html index daaed9acba..914ac5ffec 100644 --- a/docs/_includes/partials/javascript/global.html +++ b/docs/_includes/partials/javascript/global.html @@ -17,11 +17,17 @@ {# only load components that need hydrated (aka interactivity) #} + {%- if hasToc %} + + + {%- endif %} + diff --git a/docs/_plugins/tokens.cjs b/docs/_plugins/tokens.cjs index 4e772c9c5b..d686f5806d 100644 --- a/docs/_plugins/tokens.cjs +++ b/docs/_plugins/tokens.cjs @@ -118,15 +118,15 @@ function table({ tokens, name = '', docs, options } = {}) { - --${token.name} + --${token.name} - ${( isDimension ? `${token.$value}` - : isColor ? `${token.$value} ` + ${( isDimension ? `${token.$value}` + : isColor ? `${token.$value} ` : isWeight ? ` - ${token.$value} - ${token.attributes?.aliases?.[0] ?? ''}` - : `${token.$value}` + ${token.$value} + ${token.attributes?.aliases?.[0] ?? ''}` + : `${token.$value}` )} ${token.$description ?? ''} @@ -138,9 +138,7 @@ function table({ tokens, name = '', docs, options } = {}) { Color function variants + style="${styleMap({ '--samp-color': isColor ? token.$value : 'initial' })}"> @@ -149,12 +147,12 @@ function table({ tokens, name = '', docs, options } = {}) { - - + + @@ -163,9 +161,9 @@ function table({ tokens, name = '', docs, options } = {}) { - + ${copyCell(token)} diff --git a/docs/_plugins/tokensHelpers.cjs b/docs/_plugins/tokensHelpers.cjs index 3980b4edf4..72c9d64bc0 100644 --- a/docs/_plugins/tokensHelpers.cjs +++ b/docs/_plugins/tokensHelpers.cjs @@ -77,11 +77,7 @@ function copyCell(token) { `; diff --git a/docs/assets/javascript/elements/uxdot-best-practice.css b/docs/assets/javascript/elements/uxdot-best-practice.css new file mode 100644 index 0000000000..ece6ba3cff --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-best-practice.css @@ -0,0 +1,40 @@ +:host { + display: block; + margin-block: var(--rh-space-2xl); +} + +#container { + display: flex; + flex-direction: column; + gap: var(--rh-space-2xl); + margin-block: var(--rh-space-2xl); +} + +span { + font-family: var(--rh-font-family-heading); + font-size: var(--rh-font-size-heading-xs); + font-weight: var(--rh-font-weight-heading-medium); + display: flex; + flex-direction: row; + align-items: center; + gap: var(--rh-space-sm); + color: var(--_bp-color); +} + +#do { + /* TODO: remove value after https://github.com/RedHat-UX/red-hat-design-tokens/pull/138 */ + --_bp-color: var(--rh-color-status-success-on-light, #3d7317); +} + +#dont { + /* TODO: remove value after https://github.com/RedHat-UX/red-hat-design-tokens/pull/138 */ + --_bp-color: var(--rh-color-status-danger-on-light, #b1380b); +} + +::slotted(uxdot-example) { + margin: 0 !important; +} + +figure { + margin: 0 !important; +} diff --git a/docs/assets/javascript/elements/uxdot-best-practice.js b/docs/assets/javascript/elements/uxdot-best-practice.js deleted file mode 100644 index ba901cd88b..0000000000 --- a/docs/assets/javascript/elements/uxdot-best-practice.js +++ /dev/null @@ -1,75 +0,0 @@ -import { LitElement, html, css } from 'lit'; - -class UxdotBestPractice extends LitElement { - static styles = css` - :host { - display: block; - margin-block: var(--rh-space-2xl, 32px); - } - - #container { - display: flex; - flex-direction: column; - gap: var(--rh-space-2xl, 32px); - margin-block: var(--rh-space-2xl, 32px); - } - - [part="do"] { - --_color: var(--rh-color-status-success-on-light, #3d7317); - } - - [part="dont"] { - --_color: var(--rh-color-status-danger-on-light, #b1380b); - } - - span { - font-family: var(--rh-font-family-heading, RedHatDisplay, 'Red Hat Display', 'Noto Sans Arabic', 'Noto Sans Hebrew', 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans Malayalam', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans Thai', Helvetica, Arial, sans-serif); - font-size: var(--rh-font-size-heading-xs, 1.25rem); - font-weight: var(--rh-font-weight-heading-medium, 500); - display: flex; - flex-direction: row; - align-items: center; - gap: var(--rh-space-sm, 4px); - } - - rh-icon, - span { - color: var(--_color); - } - - ::slotted(uxdot-example) { - margin: 0 !important; - } - - figure { - margin: 0 !important; - } - `; - - static properties = { - do: { type: Boolean }, - dont: { type: Boolean }, - }; - - render() { - return html` -
- - ${this.do ? html` -
- Do - -
- ` : html``} - ${this.dont && !this.do ? html` -
- Don't - -
- ` : html``} -
- `; - } -} - -customElements.define('uxdot-best-practice', UxdotBestPractice); diff --git a/docs/assets/javascript/elements/uxdot-best-practice.ts b/docs/assets/javascript/elements/uxdot-best-practice.ts new file mode 100644 index 0000000000..45076036ba --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-best-practice.ts @@ -0,0 +1,29 @@ +import { LitElement, html } from 'lit'; + +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; + +import styles from './uxdot-best-practice.css'; + +@customElement('uxdot-best-practice') +export class UxdotBestPractice extends LitElement { + static styles = [styles]; + + @property({ reflect: true }) variant: 'do' | 'dont' = 'do'; + + render() { + const { variant } = this; + const icon = variant === 'do' ? 'check-circle' : 'close-circle'; + const title = variant === 'do' ? 'Do' : 'Don\'t'; + return html` +
+ +
+ ${title} + +
+
+ `; + } +} + diff --git a/docs/assets/javascript/elements/uxdot-copy-button.css b/docs/assets/javascript/elements/uxdot-copy-button.css new file mode 100644 index 0000000000..8950174406 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-copy-button.css @@ -0,0 +1,37 @@ +button { + color: inherit; + border-radius: var(--rh-border-radius-default); + border-width: 0; + background: none; + display: inline-flex; + align-items: center; + gap: var(--rh-space-xs); + padding-inline: var(--rh-space-xs); +} + +code { + padding: var(--rh-space-xs) var(--rh-space-md); + background: var(--rh-color-surface-light); + font-size: var(--rh-font-size-code-md); + font-weight: var(--rh-font-weight-code-regular); + font-family: var(--rh-font-family-code); + line-height: var(--rh-line-height-code); +} + +:host(:empty) code { + display: none; +} + +:is(rh-icon, #caption) { + display: none; +} + +:host(:state(rendered)) button:is(:focus, :active, :hover), +:host(:state(rendered)) button:is(:focus, :active, :hover) code { + background: var(--rh-color-interactive-blue-lightest); + opacity: 1; +} + +:host(:state(rendered)) :is(rh-icon, #caption) { + display: initial; +} diff --git a/docs/assets/javascript/elements/uxdot-copy-button.js b/docs/assets/javascript/elements/uxdot-copy-button.js deleted file mode 100644 index c2281fe486..0000000000 --- a/docs/assets/javascript/elements/uxdot-copy-button.js +++ /dev/null @@ -1,60 +0,0 @@ -import { html, css, LitElement, isServer } from 'lit'; - -import { RhAlert } from '@rhds/elements/rh-alert/rh-alert.js'; - -if (!isServer) { - import('@rhds/elements/rh-tooltip/rh-tooltip.js'); -} - - -export class UxdotCopyButton extends LitElement { - static is = 'uxdot-copy-button'; - static properties = { copy: {} }; - static styles = css` - button { - display: inline-flex; - align-items: center; - color: inherit; - border-radius: var(--rh-border-radius-default); - border-width: 0; - padding: var(--rh-space-xs); - background: none; - cursor: pointer; - &:is(:focus, :active, :hover) { - background: var(--rh-color-interactive-blue-lightest); - opacity: 1; - } - } - svg, - ::slotted(svg) { - width: 24px; - fill: currentcolor; - } - `; - - render() { - return html` - - ${this.copy ?? 'Click to copy'} - - - `; - } - - async #onClick() { - const text = this.copy ?? this.textContent; - const message = text.trim(); - await navigator.clipboard.writeText(message); - RhAlert.toast({ heading: 'Copied', message }); - } - - static { - customElements.define(this.is, this); - } -} diff --git a/docs/assets/javascript/elements/uxdot-copy-button.ts b/docs/assets/javascript/elements/uxdot-copy-button.ts new file mode 100644 index 0000000000..bd618b9eb9 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-copy-button.ts @@ -0,0 +1,47 @@ +import type { IconNameFor } from '@rhds/icons'; + +import { html, LitElement } from 'lit'; + +import { property } from 'lit/decorators/property.js'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import { RhAlert } from '@rhds/elements/rh-alert/rh-alert.js'; + +import '@rhds/elements/rh-tooltip/rh-tooltip.js'; +import '@rhds/elements/rh-icon/rh-icon.js'; + +import styles from './uxdot-copy-button.css'; + +@customElement('uxdot-copy-button') +export class UxdotCopyButton extends LitElement { + static styles = [styles]; + + @property() copy?: string; + + @property() icon?: IconNameFor<'ui'> = 'copy'; + + #internals = this.attachInternals(); + + render() { + return html` + + ${this.copy ?? 'Click to copy'} + + + `; + } + + firstUpdated() { + this.#internals.states.add('rendered'); + } + + async #onClick() { + const text = this.copy ?? this.textContent ?? ''; + const message = text.trim(); + await navigator.clipboard.writeText(message); + RhAlert.toast({ heading: 'Copied', message }); + } +} diff --git a/docs/assets/javascript/elements/uxdot-copy-permalink.css b/docs/assets/javascript/elements/uxdot-copy-permalink.css new file mode 100644 index 0000000000..09a4a3538d --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-copy-permalink.css @@ -0,0 +1,34 @@ +:host { + display: flex; + margin-block-end: var(--rh-space-lg); + align-items: center; +} + +#signifier { + display: var(--perma-signifier-display, none); +} + +#button { + height: 1.75rem; + background: none; + border: none; + border-radius: var(--rh-border-radius-default); + display: none; + align-items: center; +} + +#button:is(:hover, :focus) { + background: var(--rh-color-surface-light); +} + +:host(:state(rendered)) #button { + display: flex; +} + +::slotted(:is(h1, h2, h3, h4, h5, h6)) { + display: flex; + margin-block-end: 0 !important; + align-items: center; + color: inherit; + text-decoration: inherit; +} diff --git a/docs/assets/javascript/elements/uxdot-copy-permalink.js b/docs/assets/javascript/elements/uxdot-copy-permalink.js deleted file mode 100644 index 425928bf6b..0000000000 --- a/docs/assets/javascript/elements/uxdot-copy-permalink.js +++ /dev/null @@ -1,96 +0,0 @@ - -import { LitElement, html, css, isServer } from 'lit'; - -import { RhAlert } from '@rhds/elements/rh-alert/rh-alert.js'; - -class UxdotCopyPermalink extends LitElement { - static styles = css` - :host { - display: flex; - margin-block-end: var(--rh-space-lg, 16px); - align-items: center; - } - - svg { - width: var(--rh-size-icon-02, 24px); - } - - #signifier { - display: var(--perma-signifier-display, none); - } - - #button { - height: 1.75rem; - background: none; - border: none; - display: flex; - align-items: center; - } - - #button:is(:hover, :focus) { - background: var(--rh-color-surface-light, #e0e0e0); - } - - ::slotted(:is(h1, h2, h3, h4, h5, h6)) { - display: flex; - margin-block-end: 0 !important; - align-items: center; - color: inherit; - text-decoration: inherit; - } - `; - - static properties = { - copyButtonLabel: { type: String, attribute: 'copy-button-label' }, - copiedText: { type: String, attribute: 'copied-text' }, - }; - - constructor() { - super(); - this.copyButtonLabel = this.getAttribute('copy-button-label') ?? 'Copy link to clipboard'; - this.copiedText = this.getAttribute('copied-text') ?? 'Link copied'; - this.allAnchors = null; - } - - connectedCallback() { - super.connectedCallback(); - if (!isServer) { - const children = this.shadowRoot.querySelector('slot').assignedElements({ flatten: true }); - this.allAnchors = this.#getLinks(children); - } - } - - render() { - return html` - - (permalink) - - `; - } - - #handleSlotchange(e) { - const children = e.target.assignedElements({ flatten: true }); - this.allAnchors = this.#getLinks(children); - } - - #getLinks(slottedChildren) { - return slottedChildren.map(child => { - return child.querySelector('a'); - }); - } - - async #copyLink() { - const [href] = this.allAnchors; - if (href) { - await navigator.clipboard.writeText(href); - RhAlert.toast({ heading: this.copiedText }); - } - } -} - -customElements.define('uxdot-copy-permalink', UxdotCopyPermalink); diff --git a/docs/assets/javascript/elements/uxdot-copy-permalink.ts b/docs/assets/javascript/elements/uxdot-copy-permalink.ts new file mode 100644 index 0000000000..cab0cbaf50 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-copy-permalink.ts @@ -0,0 +1,48 @@ +import { LitElement, html } from 'lit'; + +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; + +import { RhAlert } from '@rhds/elements/rh-alert/rh-alert.js'; +import '@rhds/elements/rh-icon/rh-icon.js'; + +import styles from './uxdot-copy-permalink.css'; + +@customElement('uxdot-copy-permalink') +export class UxdotCopyPermalink extends LitElement { + static styles = [styles]; + + @property({ attribute: 'copy-button-label' }) copyButtonLabel = 'Copy link to clipboard'; + + @property({ attribute: 'copied-text' }) copiedText = 'Link copied'; + + #internals = this.attachInternals(); + + render() { + return html` + + (permalink) + + `; + } + + firstUpdated() { + this.#internals.states.add('rendered'); + } + + async #copyLink() { + const [link] = this.shadowRoot + ?.querySelector('slot') + ?.assignedElements({ flatten: true }) + ?.map(child => child.querySelector('a')) + ?.filter(x => !!x) ?? []; + if (link) { + await navigator.clipboard.writeText(link.href); + RhAlert.toast({ message: this.copiedText }); + } + } +} diff --git a/docs/assets/javascript/elements/uxdot-feedback.css b/docs/assets/javascript/elements/uxdot-feedback.css new file mode 100644 index 0000000000..69ae18337c --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-feedback.css @@ -0,0 +1,32 @@ +:host { + display: block; + container-type: inline-size; + container-name: host; + margin-block-start: var(--rh-space-5xl); +} + +#container { + display: grid; + grid-template-columns: 1fr; + grid-gap: var(--rh-space-3xl); + margin-block-end: var(--rh-space-3xl); +} + +h2, +::slotted(h2) { + font-family: var(--rh-font-family-heading); + font-size: var(--rh-font-size-heading-md) !important; + font-weight: var(--rh-font-weight-heading-medium); + line-height: var(--rh-line-height-heading); + margin: var(--rh-space-2xl) 0 !important; +} + +p { + font-size: var(--rh-font-size-body-text-lg); +} + +@container host (min-width: 576px) { + #container { + grid-template-columns: 1fr 1fr; + } +} diff --git a/docs/assets/javascript/elements/uxdot-feedback.js b/docs/assets/javascript/elements/uxdot-feedback.js deleted file mode 100644 index ecfc03d651..0000000000 --- a/docs/assets/javascript/elements/uxdot-feedback.js +++ /dev/null @@ -1,61 +0,0 @@ -import { LitElement, html, css } from 'lit'; - -class UxdotFeedback extends LitElement { - static styles = css` - - :host { - display: block; - container-type: inline-size; - container-name: host; - margin-block-start:var(--rh-space-5xl, 80px); - } - - #container { - display: grid; - grid-template-columns: 1fr; - grid-gap: var(--rh-space-3xl, 48px); - margin-block-end: var(--rh-space-3xl, 48px); - } - - h2, - ::slotted(h2) { - font-family: var(--rh-font-family-heading, RedHatDisplay, 'Red Hat Display', 'Noto Sans Arabic', 'Noto Sans Hebrew', 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans Malayalam', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans Thai', Helvetica, Arial, sans-serif); - font-size: var(--rh-font-size-heading-md, 1.75rem) !important; - font-weight: var(--rh-font-weight-heading-medium, 500); - line-height: var(--rh-line-height-heading, 1.3); - margin: var(--rh-space-2xl, 32px) 0 !important; - } - - p { - font-size: var(--rh-font-size-body-text-lg, 1.125rem) - } - - @container host (min-width: 576px) { - #container { - grid-template-columns: 1fr 1fr; - } - } - `; - - render() { - return html` -
-
- -

Other libraries

-

To learn more about our other libraries, visit this page.

-
-
-
-

Feedback

-

- To give feedback about anything on this page, - . -

-
-
- `; - } -} - -customElements.define('uxdot-feedback', UxdotFeedback); diff --git a/docs/assets/javascript/elements/uxdot-feedback.ts b/docs/assets/javascript/elements/uxdot-feedback.ts new file mode 100644 index 0000000000..d278a75af9 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-feedback.ts @@ -0,0 +1,31 @@ +import { LitElement, html } from 'lit'; + +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './uxdot-feedback.css'; + +@customElement('uxdot-feedback') +export class UxdotFeedback extends LitElement { + static styles = [styles]; + + render() { + return html` +
+
+ +

Other libraries

+

To learn more about our other libraries, visit this page.

+
+
+
+

Feedback

+

+ To give feedback about anything on this page, + . +

+
+
+ `; + } +} + diff --git a/docs/assets/javascript/elements/uxdot-header.css b/docs/assets/javascript/elements/uxdot-header.css new file mode 100644 index 0000000000..baad124933 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-header.css @@ -0,0 +1,74 @@ +:host { + display: block; + background-color: var(--rh-color-surface-lighter, #f2f2f2); + color: var(--rh-color-text-primary-on-light); + container-type: inline-size; + container-name: header; +} + +#container { + display: block; + z-index: 2; + max-width: 1140px; + padding-block-start: var(--_uxdot-header-padding, var(--rh-space-2xl)); + padding-inline: var(--_uxdot-header-padding, var(--rh-space-2xl)); +} + +#container:not(.hasSubnav) { + padding-block-end: var(--_uxdot-header-padding, var(--rh-space-2xl)); +} + +#container.hasSearch { + display: grid; + grid-template-columns: 1fr; + grid-template-areas: "heading" "search"; + gap: var(--rh-space-2xl); +} + +[part="heading"] { + grid-area: heading; +} + +::slotted([slot="search"]) { + grid-area: search; + display: flex; + flex-direction: column; + justify-content: space-around; +} + +::slotted(h1) { + font-family: var(--uxdot-heading-font-family, var(--rh-font-family-heading)) !important; + font-size: var(--uxdot-heading-heading-size, var(--rh-font-size-heading-2xl)) !important; + font-weight: var(--uxdot-heading-font-weight, var(--rh-font-weight-heading-regular)) !important; + line-height: var(--uxdot-heading-line-height, var(--rh-line-height-heading)) !important; +} + +::slotted([slot="subnav"]) { + margin-block-start: var(--rh-space-2xl); + inset-inline-start: calc(-1 * var(--rh-space-2xl)); + position: relative; + width: calc(100% + var(--_uxdot-header-padding, var(--rh-space-2xl)) * 2); +} + +@container header (min-width: 768px) { + #container { + --_uxdot-header-padding: var(--rh-space-3xl); + } +} + +@container header (min-width: 992px) { + :host { + margin-block-end: var(--rh-space-6xl); + } + + #container { + top: 100px; + + --_uxdot-header-padding: var(--rh-space-6xl); + } + + #container.hasSearch { + grid-template-columns: 1fr 1fr; + grid-template-areas: "heading search"; + } +} diff --git a/docs/assets/javascript/elements/uxdot-header.js b/docs/assets/javascript/elements/uxdot-header.js deleted file mode 100644 index 4c23aff9dd..0000000000 --- a/docs/assets/javascript/elements/uxdot-header.js +++ /dev/null @@ -1,106 +0,0 @@ -import { LitElement, html, css } from 'lit'; -import { classMap } from 'lit/directives/class-map.js'; - -class UxdotHeader extends LitElement { - static styles = css` - :host { - display: block; - background-color: var(--rh-color-surface-lighter, #f2f2f2); - color: var(--rh-color-text-primary-on-light, #151515); - container-type: inline-size; - container-name: header; - } - - #container { - display: block; - z-index: 2; - max-width: 1140px; - padding-block-start: var(--rh-space-2xl, 32px); - padding-inline: var(--rh-space-2xl, 32px); - } - - #container:not(.has-subnav) { - padding-block-end: var(--rh-space-2xl, 32px); - } - - #container.has-search { - display: grid; - grid-template-columns: 1fr; - grid-template-areas: 'heading' 'search'; - gap: var(--rh-space-2xl, 32px); - } - - [part="heading"] { - grid-area: heading; - } - - ::slotted([slot="search"]) { - grid-area: search; - display: flex; - flex-direction: column; - justify-content: space-around; - } - - ::slotted(h1) { - font-family: var(--uxdot-heading-font-family, var(--rh-font-family-heading, RedHatDisplay, "Red Hat Display", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif)) !important; - font-size: var(--uxdot-heading-heading-size, var(--rh-font-size-heading-2xl, 3rem)) !important; - font-weight: var(--uxdot-heading-font-weight, var(--rh-font-weight-heading-regular, 300)) !important; - line-height: var(--uxdot-heading-line-height, var(--rh-line-height-heading, 1.3)) !important; - } - - ::slotted([slot="subnav"]) { - margin-block-start: var(--rh-space-2xl, 32px); - } - - @container header (min-width: 768px) { - #container { - padding-block-start: var(--rh-space-3xl, 48px); - padding-inline: var(--rh-space-3xl, 48px); - } - - #container:not(.has-subnav) { - padding-block-end: var(--rh-space-3xl, 48px); - } - } - - @container header (min-width: 992px) { - :host { - margin-block-end: var(--rh-space-6xl, 96px); - } - - #container { - top: 100px; - padding-inline: var(--rh-space-6xl, 96px); - padding-block-start: var(--rh-space-6xl, 96px); - } - - #container:not(.has-subnav) { - padding-block-end: var(--rh-space-6xl, 96px); - } - - #container.has-search { - grid-template-columns: 1fr 1fr; - grid-template-areas: 'heading search'; - } - } - `; - - static properties = { - hasSubNav: { type: Boolean, attribute: 'has-subnav' }, - hasSearch: { type: Boolean, attribute: 'has-search' }, - }; - - - render() { - const classes = classMap({ 'has-subnav': this.hasSubNav, 'has-search': this.hasSearch }); - return html` -
- - - -
- `; - } -} - -customElements.define('uxdot-header', UxdotHeader); diff --git a/docs/assets/javascript/elements/uxdot-header.ts b/docs/assets/javascript/elements/uxdot-header.ts new file mode 100644 index 0000000000..cedc62327f --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-header.ts @@ -0,0 +1,28 @@ +import { LitElement, html } from 'lit'; +import { classMap } from 'lit/directives/class-map.js'; + +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; + +import styles from './uxdot-header.css'; + +@customElement('uxdot-header') +export class UxdotHeader extends LitElement { + static styles = [styles]; + + @property({ type: Boolean, attribute: 'has-subnav' }) hasSubnav = false; + + @property({ type: Boolean, attribute: 'has-search' }) hasSearch = false; + + render() { + const { hasSubnav, hasSearch } = this; + return html` +
+ + + +
+ `; + } +} + diff --git a/docs/assets/javascript/elements/uxdot-hero.css b/docs/assets/javascript/elements/uxdot-hero.css new file mode 100644 index 0000000000..f807d2a9c4 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-hero.css @@ -0,0 +1,54 @@ +:host { + display: block; +} + +:host([compact]) { + margin-block-end: var(--rh-space-3xl); +} + +#container { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +slot[name="header"]::slotted(*) { + margin-block: 0 !important; +} + +slot[name="header"]::slotted(h1) { + color: var(--rh-color-text-brand-on-light); + margin-block-end: 0; + text-transform: uppercase; + font-family: var(--rh-font-family-heading); + line-height: var(--rh-line-height-heading); + font-size: var(--rh-font-size-code-lg) !important; + font-weight: var(--rh-font-weight-code-medium); +} + +slot[name="tagline"]::slotted(p) { + font-size: var(--rh-font-size-heading-2xl) !important; + margin-block: var(--rh-space-lg) !important; + text-align: center; +} + +slot[name="image"]::slotted(img) { + width: 100%; + margin-block-start: var(--rh-space-4xl); +} + +:host slot[name="header"]::slotted(h2) { + color: var(--rh-color-text-brand-on-light); + font-family: var(--rh-font-family-heading); + font-size: var(--rh-font-size-heading-xl) !important; + font-weight: var(--rh-font-weight-heading-regular) !important; + margin-block-end: var(--rh-space-2xl) !important; + text-align: center; +} + +:host([compact]) ::slotted(p) { + font-size: var(--rh-font-size-body-text-lg); + text-align: center; + max-width: 62rem; +} diff --git a/docs/assets/javascript/elements/uxdot-hero.js b/docs/assets/javascript/elements/uxdot-hero.js deleted file mode 100644 index b817138686..0000000000 --- a/docs/assets/javascript/elements/uxdot-hero.js +++ /dev/null @@ -1,73 +0,0 @@ -import { LitElement, html, css } from 'lit'; - -class UxdotHero extends LitElement { - static styles = css` - :host { - display: block; - } - - :host([compact]) { - margin-block-end: var(--rh-space-3xl, 48px); - } - - #container { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - } - - slot[name="header"]::slotted(*) { - margin-block: 0 !important; - } - - slot[name="header"]::slotted(h1) { - color: var(--rh-color-text-brand-on-light, #ee0000); - margin-block-end: 0; - text-transform: uppercase; - font-family: var(--rh-font-family-heading, RedHatDisplay, "Red Hat Display", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); - line-height: var(--rh-line-height-heading, 1.3); - font-size: var(--rh-font-size-code-lg, 1.125rem) !important; - font-weight: var(--rh-font-weight-code-medium, 500); - } - - slot[name="tagline"]::slotted(p) { - font-size: var(--rh-font-size-heading-2xl, 3rem) !important; - margin-block: var(--rh-space-lg, 16px) !important; - text-align: center; - } - - slot[name="image"]::slotted(img) { - width: 100%; - margin-block-start: var(--rh-space-4xl, 64px); - } - - :host slot[name="header"]::slotted(h2) { - color: var(--rh-color-text-brand-on-light, #ee0000); - font-family: var(--rh-font-family-heading, RedHatDisplay, "Red Hat Display", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); - font-size: var(--rh-font-size-heading-xl, 2.5rem) !important; - font-weight: var(--rh-font-weight-heading-regular, 300) !important; - margin-block-end: var(--rh-space-2xl, 32px) !important; - text-align: center; - } - - :host([compact]) ::slotted(p) { - font-size: var(--rh-font-size-body-text-lg, 1.125rem); - text-align: center; - max-width: 62rem; - } - `; - - render() { - return html` -
- - - - -
- `; - } -} - -customElements.define('uxdot-hero', UxdotHero); diff --git a/docs/assets/javascript/elements/uxdot-hero.ts b/docs/assets/javascript/elements/uxdot-hero.ts new file mode 100644 index 0000000000..6148202c75 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-hero.ts @@ -0,0 +1,22 @@ +import { LitElement, html } from 'lit'; + +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './uxdot-hero.css'; + +@customElement('uxdot-hero') +export class UxdotHero extends LitElement { + static styles = [styles]; + + render() { + return html` +
+ + + + +
+ `; + } +} + diff --git a/docs/assets/javascript/elements/uxdot-masthead.css b/docs/assets/javascript/elements/uxdot-masthead.css new file mode 100644 index 0000000000..7fddc3b448 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-masthead.css @@ -0,0 +1,93 @@ +:host { + display: block; + background-color: var(--rh-color-surface-darkest); + color: var(--rh-color-text-primary-on-dark); + position: fixed; + inset: 0; + height: max-content; + z-index: var(--uxdot-masthead-z-index, 2); + container-type: inline-size; + container-name: host; +} + +#container { + display: grid; + column-gap: var(--rh-space-lg); + grid-template-columns: max-content 1fr max-content; + max-height: var(--uxdot-masthead-max-height, 72px); + margin-inline: var(--rh-space-md); + margin-block: var(--rh-space-lg); +} + +slot[name="hamburger"] { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--rh-space-md); + justify-content: center; +} + +slot[name="hamburger"]::slotted(button) { + color: var(--rh-color-text-primary-on-dark); + background-color: transparent; + border: none; + margin: 0; + padding: var(--rh-space-md); + line-height: 0 !important; +} + +slot[name="hamburger"]:hover::slotted(button), +slot[name="hamburger"]::slotted(button:hover), +slot[name="hamburger"]::slotted(button:active), +slot[name="hamburger"]::slotted(button:focus) { + color: var(--rh-color-icon-subtle-hover); +} + +slot[name="hamburger"]::slotted(button:focus) { + outline: var(--rh-border-width-md) solid var(--rh-color-border-interactive-on-dark); + border-radius: var(--rh-border-radius-default); +} + +slot[name="logo"]::slotted(a) { + display: flex; + flex-direction: row; + align-items: center; + justify-self: flex-start; + gap: var(--rh-space-md); +} + +slot[name="links"] { + --rh-icon-size: 24px; + + display: flex; + flex-direction: row; + column-gap: var(--rh-space-lg); +} + +slot[name="links"]::slotted(a) { + display: flex; + flex-direction: row; + gap: var(--rh-space-lg); + align-items: center; +} + +@container (min-width: 576px) { + #container { + gap: var(--rh-space-lg); + margin: var(--rh-space-lg); + } +} + +@container (min-width: 992px) { + #container { + grid-template-columns: 1fr max-content; + } + + slot[name="hamburger"] { + display: none; + } + + slot[name="logo"]::slotted(a) { + margin-inline-start: var(--rh-space-lg); + } +} diff --git a/docs/assets/javascript/elements/uxdot-masthead.js b/docs/assets/javascript/elements/uxdot-masthead.js deleted file mode 100644 index a69b6c3c91..0000000000 --- a/docs/assets/javascript/elements/uxdot-masthead.js +++ /dev/null @@ -1,111 +0,0 @@ -import { LitElement, html, css } from 'lit'; - -class UxdotMasthead extends LitElement { - static styles = css` - :host { - display: block; - background-color: var(--rh-color-surface-darkest, #151515); - color: var(--rh-color-text-primary-on-dark, #ffffff); - position: fixed; - inset: 0; - height: max-content; - z-index: var(--uxdot-masthead-z-index, 2); - container-type: inline-size; - container-name: host; - } - - #container { - display: grid; - column-gap: var(--rh-space-lg, 16px); - grid-template-columns: max-content 1fr max-content; - max-height: var(--uxdot-masthead-max-height, 72px); - margin-inline: var(--rh-space-md, 8px); - margin-block: var(--rh-space-lg, 16px); - } - - slot[name="hamburger"] { - display: flex; - flex-direction: column; - align-items: center; - gap: var(--rh-space-md, 8px); - justify-content: center; - } - - slot[name="hamburger"]::slotted(button) { - color: var(--rh-color-text-on-dark, #ffffff); - background-color: transparent; - border: none; - margin: 0; - padding: var(--rh-space-md, 8px); - line-height: 0 !important; - } - - slot[name="hamburger"]:hover::slotted(button), - slot[name="hamburger"]::slotted(button:hover), - slot[name="hamburger"]::slotted(button:active), - slot[name="hamburger"]::slotted(button:focus) { - color: var(--rh-color-icon-subtle-hover, #a3a3a3); - } - - slot[name="hamburger"]::slotted(button:focus) { - outline: var(--rh-border-width-md, 2px) solid var(--rh-color-border-interactive-on-dark, #92c5f9); - border-radius: var(--rh-border-radius-default, 3px); - } - - slot[name="logo"]::slotted(a) { - display: flex; - flex-direction: row; - align-items: center; - justify-self: flex-start; - gap: var(--rh-space-md, 8px); - } - - slot[name="links"] { - --rh-icon-size: 24px; - display: flex; - flex-direction: row; - column-gap: var(--rh-space-lg, 16px); - } - - slot[name="links"]::slotted(a) { - display: flex; - flex-direction: row; - gap: var(--rh-space-lg, 16px); - align-items: center; - } - - @container (min-width: 576px) { - #container { - gap: var(--rh-space-lg, 16px); - margin: var(--rh-space-lg, 16px); - } - } - - @container (min-width: 992px) { - #container { - grid-template-columns: 1fr max-content; - } - - slot[name="hamburger"] { - display: none; - } - - slot[name="logo"]::slotted(a) { - margin-inline-start: var(--rh-space-lg, 16px); - } - } - - `; - - render() { - return html` -
- - - -
- `; - } -} - -customElements.define('uxdot-masthead', UxdotMasthead); diff --git a/docs/assets/javascript/elements/uxdot-masthead.ts b/docs/assets/javascript/elements/uxdot-masthead.ts new file mode 100644 index 0000000000..a16dfc1f63 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-masthead.ts @@ -0,0 +1,21 @@ +import { LitElement, html } from 'lit'; + +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './uxdot-masthead.css'; + +@customElement('uxdot-masthead') +export class UxdotMasthead extends LitElement { + static styles = [styles]; + + render() { + return html` +
+ + + +
+ `; + } +} + diff --git a/docs/assets/javascript/elements/uxdot-repo-status-list-lightdom.css b/docs/assets/javascript/elements/uxdot-repo-status-list-lightdom.css index e30d041904..a33e9a34f1 100644 --- a/docs/assets/javascript/elements/uxdot-repo-status-list-lightdom.css +++ b/docs/assets/javascript/elements/uxdot-repo-status-list-lightdom.css @@ -1,10 +1,10 @@ uxdot-repo-status-list > dl > div { display: flex; - column-gap: var(--rh-space-md, 8px); + column-gap: var(--rh-space-md); align-items: center; } uxdot-repo-status-list > dl > div > dt { - font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); - font-size: var(--rh-font-size-body-text-md, 1rem); + font-family: var(--rh-font-family-body-text); + font-size: var(--rh-font-size-body-text-md); } diff --git a/docs/assets/javascript/elements/uxdot-repo-status-list.css b/docs/assets/javascript/elements/uxdot-repo-status-list.css new file mode 100644 index 0000000000..212a08dae2 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-repo-status-list.css @@ -0,0 +1,43 @@ +:host { + display: block; + container-type: inline-size; + container-name: host; +} + +#inner-container { + border: var(--rh-border-width-sm); + border-radius: var(--rh-border-radius-default); + margin-block-start: var(--rh-space-lg); +} + +#header-container { + display: flex; + justify-content: space-between; + align-items: center; +} + +[name="checklist"]::slotted(a) { + font-size: var(--rh-font-size-body-text-sm); +} + +::slotted(dl) { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--rh-space-lg); + margin-block: var(--rh-space-lg) !important; + margin-inline: var(--rh-space-xl) !important; +} + +@container host (min-width: 768px) { + ::slotted(dl) { + flex-direction: row; + column-gap: var(--rh-space-lg); + } +} + +@container host (min-width: 992px) { + ::slotted(dl) { + column-gap: var(--rh-space-2xl); + } +} diff --git a/docs/assets/javascript/elements/uxdot-repo-status-list.js b/docs/assets/javascript/elements/uxdot-repo-status-list.js deleted file mode 100644 index 5672f188df..0000000000 --- a/docs/assets/javascript/elements/uxdot-repo-status-list.js +++ /dev/null @@ -1,66 +0,0 @@ -import { LitElement, html, css } from 'lit'; - -class UxdotRepoStatusList extends LitElement { - static styles = css` - :host { - display: block; - container-type: inline-size; - container-name: host; - } - - #inner-container { - border: var(--rh-border-width-sm, 1px) solid var(--rh-color-border-subtle-on-light, #c7c7c7); - border-radius: var(--rh-border-radius-default, 3px); - margin-block-start: var(--rh-space-lg, 16px); - } - - #header-container { - display: flex; - justify-content: space-between; - align-items: center; - } - - [name="checklist"]::slotted(a) { - font-size: var(--rh-font-size-body-text-sm, 0.875rem); - } - - ::slotted(dl) { - display: flex; - flex-direction: column; - align-items: flex-start; - column-gap: var(--rh-space-2xl, 32px); - row-gap: var(--rh-space-lg, 16px); - margin-block: var(--rh-space-lg, 16px) !important; - margin-inline: var(--rh-space-xl, 24px) !important; - } - - @container host (min-width: 768px) { - ::slotted(dl) { - flex-direction: row; - column-gap: var(--rh-space-lg, 16px); - } - } - - @container host (min-width: 992px) { - ::slotted(dl) { - column-gap: var(--rh-space-2xl, 32px); - } - } - `; - - render() { - return html` -
-
- - -
-
- -
-
- `; - } -} - -customElements.define('uxdot-repo-status-list', UxdotRepoStatusList); diff --git a/docs/assets/javascript/elements/uxdot-repo-status-list.ts b/docs/assets/javascript/elements/uxdot-repo-status-list.ts new file mode 100644 index 0000000000..63fac33282 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-repo-status-list.ts @@ -0,0 +1,23 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import style from './uxdot-repo-status-list.css'; + +@customElement('uxdot-repo-status-list') +export class UxdotRepoStatusList extends LitElement { + static styles = [style]; + + render() { + return html` +
+
+ + +
+
+ +
+
+ `; + } +} diff --git a/docs/assets/javascript/elements/uxdot-search.css b/docs/assets/javascript/elements/uxdot-search.css new file mode 100644 index 0000000000..c7018a87a1 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-search.css @@ -0,0 +1,67 @@ +:host { + display: grid; + gap: var(--rh-space-lg); + grid-template: 1fr / 1fr max-content; + font-family: var(--rh-font-family-body-text); + position: relative; +} + +[hidden] { display: none !important; } + +input { + border: var(--rh-border-width-sm); + border-bottom-color: var(--rh-color-gray-50); + padding: var(--rh-space-md); +} + +input:focus { + border-bottom-color: var(--rh-color-interactive-blue-darker); + border-bottom-width: var(--rh-border-width-md); +} + +input::placeholder { + font-family: inherit; + font-size: var(--rh-font-size-body-text-md); +} + +#container { + position: absolute; + max-height: 300px; + overflow-y: scroll; + z-index: 2; + grid-column: 1/2; + width: calc(100% + 2 * var(--rh-border-width-md)); + inset-block-start: var(--rh-length-2xl); + inset-inline-start: calc(-1 * var(--rh-space-md)); + padding: var(--rh-space-sm); +} + +ol { + list-style-type: none; + flex-flow: column nowrap; + padding-inline-start: 0; + border: var(--rh-border-width-sm); + background: var(--rh-color-surface-lightest); + margin: 0; + height: calc(100% - var(--rh-space-md)); + width: calc(100% - 2 * var(--rh-border-width-md)); +} + +a { + color: inherit; + text-decoration: none; + font-size: var(--rh-font-size-body-text-sm); +} + +a:focus { + outline: none; +} + +li { + padding: var(--rh-space-md); +} + +li[aria-selected="true"], +li:focus-within { + outline: var(--rh-border-width-md); +} diff --git a/docs/assets/javascript/elements/uxdot-search.js b/docs/assets/javascript/elements/uxdot-search.js deleted file mode 100644 index bb63c99cb2..0000000000 --- a/docs/assets/javascript/elements/uxdot-search.js +++ /dev/null @@ -1,248 +0,0 @@ -import { LitElement, html, css, isServer } from 'lit'; -import { ifDefined } from 'lit/directives/if-defined.js'; - - -if (!isServer) { - import('@rhds/elements/rh-button/rh-button.js'); -} - -class UxdotSearch extends LitElement { - static formAssociated = true; - - static properties = { - placeholder: {}, - value: {}, - items: { type: Array, attribute: false }, - expanded: { type: Boolean, state: true }, - activeIndex: { type: Number, state: true }, - }; - - static styles = css` - :host { - display: grid; - gap: var(--rh-space-lg, 16px); - grid-template: 1fr / 1fr max-content; - font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); - position: relative; - } - - [hidden] { display: none !important; } - - input { - border: var(--rh-border-width-sm, 1px) solid var(--rh-color-gray-20, #c7c7c7); - border-bottom-color: var(--rh-color-gray-50, #4d4d4d); - padding: var(--rh-space-md, 8px); - } - - input:focus { - border-bottom-color: var(--rh-color-interactive-blue-darker, #0066cc); - border-bottom-width: var(--rh-border-width-md, 2px); - } - - input::placeholder { - font-family: inherit; - font-size: var(--rh-font-size-body-text-md, 1rem); - } - - #container { - position: absolute; - max-height: 300px; - overflow-y: scroll; - z-index: 2; - grid-column: 1/2; - width: calc(100% + 2 * var(--rh-border-width-md, 2px)); - inset-block-start: var(--rh-length-2xl, 32px); - inset-inline-start: calc(-1 * var(--rh-space-md, 8px)); - padding: var(--rh-space-sm, 6px) var(--rh-space-md, 8px); - } - - ol { - list-style-type: none; - flex-flow: column nowrap; - padding-inline-start: 0; - border: var(--rh-border-width-sm, 1px) solid var(--rh-color-gray-20, #c7c7c7); - background: var(--rh-color-surface-lightest, #ffffff); - margin: 0; - height: calc(100% - var(--rh-space-md, 8px)); - width: calc(100% - 2 * var(--rh-border-width-md, 2px)); - } - - a { - color: inherit; - text-decoration: none; - font-size: var(--rh-font-size-body-text-sm, 0.875rem); - } - - a:focus { - outline: none; - } - - li { - padding: var(--rh-space-md, 8px); - } - - li:focus-within { - outline: var(--rh-border-width-md, 2px) solid var(--rh-color-interactive-blue-darker, #0066cc); - } - `; - - /** @type {Set} */ - static #instances = new Set(); - - static { - if (!isServer) { - window.addEventListener('click', event => { - for (const instance of this.#instances) { - instance.#onOutsideClick(event); - } - }); - } - } - - #internals = !isServer ? this.attachInternals() : null; - - #ariaLabel = ''; - - get form() { - if (isServer) { - return null; - } - return this.#internals.form; - } - - get value() { - return this.#input.value; - } - - set value(value) { - this.#input.value = value ?? ''; - } - - get #input() { - return this.shadowRoot.getElementById('input'); - } - - get #firstLink() { - return this.shadowRoot.querySelector('li a'); - } - - get #lastLink() { - return this.shadowRoot.querySelector('li:last-of-type a'); - } - - get selectedItem() { - return this.shadowRoot.querySelector('[aria-selected="true"]'); - } - - constructor() { - super(); - this.items = []; - if (!isServer) { - this.addEventListener('keydown', this.#onKeydown); - this.addEventListener('blur', this.#onBlur); - } - } - - connectedCallback() { - super.connectedCallback(); - this.#ariaLabel = this.getAttribute('aria-label') || undefined; - this.removeAttribute('aria-label'); - if (this.#ariaLabel) { - this.setAttribute('original-aria-label', this.#ariaLabel); - } - this.requestUpdate(); - } - - render() { - return html` - -
-
    - ${(this.items ?? []).map((item, i) => !item ? '' : html` -
  1. - ${item.label} -
  2. - `)} -
-
- Search - `; - } - - #onOutsideClick(event) { - if (event.composedPath().every(x => x !== this)) { - this.expanded = false; - } - } - - #onClickSearch() { - this.expanded = true; - if (this.value && !isServer) { - this.form?.requestSubmit(); - } - } - - async #onBlur() { - await this.updateComplete; - if (!this.shadowRoot.activeElement) { - await this.updateComplete; - this.expanded = false; - if (this.selectedItem) { - this.value = this.selectedItem.textContent?.trim(); - } - } - } - - #onInput() { - this.#internals.setFormValue(this.value); - if (this.value) { - this.expanded = true; - } - } - - #onKeydown(event) { - switch (event.key) { - case 'ArrowDown': - case 'ArrowUp': { - this.expanded = true; - return this.#focus(event); - } - case 'Escape': this.expanded = false; break; - case 'Enter': this.form?.requestSubmit(); - } - } - - #focus(event) { - event.preventDefault(); - this.activeIndex ??= -1; - const d = ({ ArrowDown: 1, ArrowUp: -1 })[event.key]; - const { activeElement } = this.shadowRoot; - if (activeElement === this.#input) { - ({ ArrowUp: this.#lastLink, ArrowDown: this.#firstLink })[event.key]?.focus(); - this.activeIndex = d > 0 ? 0 : this.items.length - 1; - } else if (activeElement instanceof HTMLAnchorElement) { - const nextIndex = this.activeIndex + d; - const nextItem = this.items[nextIndex]; - const nextFocus = this.shadowRoot.getElementById(`i-${nextIndex}`) ?? this.#input; - this.activeIndex = nextItem ? nextIndex : -1; - nextFocus.focus(); - } - } -} - -customElements.define('uxdot-search', UxdotSearch); diff --git a/docs/assets/javascript/elements/uxdot-search.ts b/docs/assets/javascript/elements/uxdot-search.ts new file mode 100644 index 0000000000..d37e9bf2f6 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-search.ts @@ -0,0 +1,178 @@ +import { LitElement, html, isServer } from 'lit'; +import { ifDefined } from 'lit/directives/if-defined.js'; + +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; + +import '@rhds/elements/rh-button/rh-button.js'; + +import styles from './uxdot-search.css'; + +interface Item { + label: string; + value: string; +} + +@customElement('uxdot-search') +export class UxdotSearch extends LitElement { + static formAssociated = true; + + static styles = [styles]; + + @property() placeholder?: string; + + @property({ type: Array, attribute: false }) items: Item[] = []; + + @property({ type: Boolean, state: true }) expanded = false; + + @property({ type: Number, state: true }) activeIndex?: number; + + private static instances = new Set(); + + static { + globalThis?.addEventListener?.('click', event => { + for (const instance of UxdotSearch.instances) { + instance.#onOutsideClick(event); + } + }); + } + + #internals = !isServer ? this.attachInternals() : null; + + #ariaLabel = ''; + + get form() { + return this.#internals?.form ?? null; + } + + @property() + get value() { + return this.#input?.value ?? ''; + } + + set value(value) { + if (this.#input) { + this.#input.value = value ?? ''; + } + } + + get #input() { + return this.shadowRoot?.getElementById('input') as HTMLInputElement | null ?? null; + } + + get #firstLink(): HTMLAnchorElement | null { + return this.shadowRoot?.querySelector('li a') ?? null; + } + + get #lastLink(): HTMLAnchorElement | null { + return this.shadowRoot?.querySelector('li:last-of-type a') ?? null; + } + + get selectedItem() { + return this.shadowRoot?.querySelector('[aria-selected="true"]'); + } + + connectedCallback() { + super.connectedCallback(); + this.addEventListener('keydown', this.#onKeydown); + this.addEventListener('blur', this.#onBlur); + this.#ariaLabel = this.getAttribute('aria-label') ?? ''; + this.removeAttribute('aria-label'); + if (this.#ariaLabel) { + this.setAttribute('original-aria-label', this.#ariaLabel); + } + this.requestUpdate(); + } + + render() { + return html` + +
+
    ${this.items.map((item, i) => !item ? '' : html` +
  1. + ${item.label} +
  2. `)} +
+
+ Search + `; + } + + #onOutsideClick(event: Event) { + if (event.composedPath().every(x => x !== this)) { + this.expanded = false; + } + } + + #onClickSearch() { + this.expanded = true; + if (this.value && !isServer) { + this.form?.requestSubmit(); + } + } + + async #onBlur() { + await this.updateComplete; + if (!this.shadowRoot?.activeElement) { + await this.updateComplete; + this.expanded = false; + if (this.selectedItem) { + this.value = this.selectedItem.textContent?.trim() ?? ''; + } + } + } + + #onInput() { + this.#internals?.setFormValue(this.value); + if (this.value) { + this.expanded = true; + } + } + + #onKeydown(event: KeyboardEvent) { + switch (event.key) { + case 'ArrowDown': + case 'ArrowUp': { + this.expanded = true; + return this.#focus(event); + } + case 'Escape': this.expanded = false; break; + case 'Enter': this.form?.requestSubmit(); + } + } + + #focus(event: KeyboardEvent) { + event.preventDefault(); + this.activeIndex ??= -1; + const d = ({ ArrowDown: 1, ArrowUp: -1 })[event.key]!; + const activeElement = this.shadowRoot?.activeElement; + if (activeElement === this.#input) { + ({ ArrowUp: this.#lastLink, ArrowDown: this.#firstLink })[event.key]?.focus(); + this.activeIndex = d > 0 ? 0 : this.items.length - 1; + } else if (activeElement instanceof HTMLAnchorElement) { + const nextIndex = this.activeIndex + d; + const nextItem = this.items[nextIndex]; + const nextFocus = this.shadowRoot?.getElementById(`i-${nextIndex}`) ?? this.#input; + this.activeIndex = nextItem ? nextIndex : -1; + nextFocus?.focus(); + } + } +} + diff --git a/docs/assets/javascript/elements/uxdot-sidenav-dropdown-menu-item.css b/docs/assets/javascript/elements/uxdot-sidenav-dropdown-menu-item.css new file mode 100644 index 0000000000..fcdb964306 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-sidenav-dropdown-menu-item.css @@ -0,0 +1,4 @@ +a { + padding: var(--rh-space-md) 0 var(--rh-space-md) var(--rh-space-lg); + font-size: var(--rh-font-size-body-text-md); +} diff --git a/docs/assets/javascript/elements/uxdot-sidenav-dropdown-menu.css b/docs/assets/javascript/elements/uxdot-sidenav-dropdown-menu.css new file mode 100644 index 0000000000..871bf9c324 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-sidenav-dropdown-menu.css @@ -0,0 +1,4 @@ +:host { + display: block; + padding-inline-start: var(--rh-space-2xl); +} diff --git a/docs/assets/javascript/elements/uxdot-sidenav-dropdown.css b/docs/assets/javascript/elements/uxdot-sidenav-dropdown.css new file mode 100644 index 0000000000..6a3b76c0e4 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-sidenav-dropdown.css @@ -0,0 +1,3 @@ +:host { + z-index: var(--uxdot-sidenav-z-index, 102); +} diff --git a/docs/assets/javascript/elements/uxdot-sidenav-item.css b/docs/assets/javascript/elements/uxdot-sidenav-item.css new file mode 100644 index 0000000000..f729aa5ab1 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-sidenav-item.css @@ -0,0 +1,22 @@ +:host, +a { + display: block; +} + +a { + padding: var(--rh-space-lg) var(--rh-space-2xl); + font-size: var(--rh-font-size-body-text-lg); + text-decoration: none !important; + color: var(--rh-color-text-primary-on-light) !important; + border-inline-start: var(--rh-border-width-lg) solid transparent; +} + +a:hover { + background: var(--rh-color-surface-lighter); + border-inline-start-color: var(--rh-color-border-subtle-on-light); +} + +a.active { + background: var(--rh-color-surface-lighter); + border-inline-start-color: var(--rh-color-accent-brand-on-light); +} diff --git a/docs/assets/javascript/elements/uxdot-sidenav-lightdom.css b/docs/assets/javascript/elements/uxdot-sidenav-lightdom.css index 9378e0e078..795aaff5c8 100644 --- a/docs/assets/javascript/elements/uxdot-sidenav-lightdom.css +++ b/docs/assets/javascript/elements/uxdot-sidenav-lightdom.css @@ -1,7 +1,3 @@ -uxdot-sidenav > ul > li { - margin-block: 0; -} - uxdot-sidenav-dropdown > details > summary { display: flex; flex-direction: row; @@ -13,13 +9,9 @@ uxdot-sidenav-dropdown > details > summary { solid transparent; text-decoration: none; - color: var(--rh-color-text-primary-on-light, #151515); - font-size: var(--rh-font-size-body-text-lg, 1.125rem); - padding: var(--rh-space-lg, 16px) var(--rh-space-2xl, 32px); -} - -uxdot-sidenav-dropdown-menu > ul > li { - margin-block: 0; + color: var(--rh-color-text-primary-on-light); + font-size: var(--rh-font-size-body-text-lg); + padding: var(--rh-space-lg) var(--rh-space-2xl); } uxdot-sidenav-dropdown > details > summary::-webkit-details-marker { @@ -29,24 +21,18 @@ uxdot-sidenav-dropdown > details > summary::-webkit-details-marker { uxdot-sidenav-dropdown > details > summary:hover, uxdot-sidenav-dropdown > details[open] > summary:focus, uxdot-sidenav-dropdown > details[open] > summary:hover { - background: var(--rh-color-surface-lighter, #f2f2f2); - border-inline-start-color: var(--rh-color-border-subtle-on-light, #c7c7c7); + background: var(--rh-color-surface-lighter); + border-inline-start-color: var(--rh-color-border-subtle-on-light); } uxdot-sidenav-dropdown > details > summary:after { content: ""; - color: var(--rh-color-text-primary-on-light, #151515); + color: var(--rh-color-text-primary-on-light); width: 9px; height: 9px; transform: rotate(45deg); - border-right: - var(--rh-border-width-lg, 3px) - solid - var(--rh-color-border-strong-on-light, #151515); - border-top: - var(--rh-border-width-lg, 3px) - solid - var(--rh-color-border-strong-on-light, #151515); + border-right: var(--rh-border-width-lg) solid var(--rh-color-border-strong-on-light); + border-top: var(--rh-border-width-lg) solid var(--rh-color-border-strong-on-light); } uxdot-sidenav-dropdown > details[open] > summary:after { diff --git a/docs/assets/javascript/elements/uxdot-sidenav.css b/docs/assets/javascript/elements/uxdot-sidenav.css new file mode 100644 index 0000000000..ed2a521190 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-sidenav.css @@ -0,0 +1,119 @@ +:host { + --_padding-start: var(--uxdot-sidenav-padding-start, var(--rh-space-2xl)); + --_padding-end: var(--uxdot-sidenav-padding-end, var(--rh-space-2xl)); + --_max-height: 100dvh; + + width: 100%; + height: var(--_max-height); + top: 0; + z-index: var(--uxdot-sidenav-z-index, 2); + overflow: scroll; +} + +:host([open]) { + display: block !important; + position: fixed !important; +} + +:host(:not([open])) { + display: none; +} + +[part="close-button"] { + color: var(--rh-color-text-primary-on-light); + background-color: transparent; + border: none; + margin: 0; + padding: var(--rh-space-md); + line-height: 0 !important; +} + +#container { + position: relative; + background-color: var(--rh-color-surface-lightest); + width: auto; + height: 100%; +} + +#close-button-container { + padding-inline: var(--rh-space-md); + padding-block: var(--rh-space-lg); + max-height: var(--uxdot-masthead-max-height, 72px); +} + +#close-button:focus { + outline: var(--rh-border-width-md) solid var(--rh-color-border-interactive-on-light); + border-radius: var(--rh-border-radius-default, 3px); +} + +[part="overlay"] { + --_gray-90-rgb: var(--rh-color-gray-90-rgb); + + display: none; + background-color: rgb(var(--_gray-90-rgb) / var(--rh-opacity-60)); + position: fixed; + top: 0; + height: 100dvh; + width: 100dvw; + z-index: -1; +} + +::slotted(ul) { + padding-inline: 0; + padding-block: + var(--_padding-start) + calc(var(--_padding-end) + (var(--rh-font-size-body-text-lg) * 1.5) + (var(--rh-space-lg) * 2)); + + /* allow overflow to scroll with --_padding-end + section item height */ + list-style: none; + margin-block: 0 !important; + height: var(--_max-height); + overflow-y: scroll; + background-color: var(--rh-color-surface-lightest); +} + +:host([open]) [part="overlay"] { + display: block; +} + +@media (min-width: 320px) { + :host { + --uxdot-sidenav-width: 320px; + + width: var(--uxdot-sidenav-width); + box-shadow: var(--rh-box-shadow-lg); + } + + #container { + width: var(--uxdot-sidenav-width, 320px); + } +} + +@media (min-width: 576px) { + #close-button-container { + padding: var(--rh-space-lg); + } +} + +@media (min-width: 992px) { + :host { + --uxdot-siznav-z-index: 1; + + position: fixed; + top: var(--uxdot-masthead-max-height, 72px); + height: calc(var(--_max-height) - var(--uxdot-masthead-max-height, 72px)); + box-shadow: unset; + } + + :host(:not([open])) { + display: block; + } + + #close-button-container { + display: none; + } + + :host([open]) [part="overlay"] { + display: none; + } +} diff --git a/docs/assets/javascript/elements/uxdot-sidenav.js b/docs/assets/javascript/elements/uxdot-sidenav.js deleted file mode 100644 index 1723eee743..0000000000 --- a/docs/assets/javascript/elements/uxdot-sidenav.js +++ /dev/null @@ -1,473 +0,0 @@ -import { LitElement, html, css, isServer } from 'lit'; - -import { RovingTabindexController } from '@patternfly/pfe-core/controllers/roving-tabindex-controller.js'; - -import '@rhds/elements/rh-icon/rh-icon.js'; - -class SSRRoleElement extends LitElement { - static properties = { - role: { reflect: true }, - }; -} - -/* ************* */ -/* UXDOT-SIDENAV */ -/* ************* */ -class UxdotSideNav extends SSRRoleElement { - static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }; - static properties = { - open: { type: Boolean, reflect: true }, - trigger: { type: String }, - }; - - static styles = css` - :host { - --_padding-start: var(--uxdot-sidenav-padding-start, var(--rh-space-2xl, 32px)); - --_padding-end: var(--uxdot-sidenav-padding-end, var(--rh-space-2xl, 32px)); - --_max-height: 100dvh; - - width: 100%; - height: var(--_max-height); - top: 0; - z-index: var(--uxdot-sidenav-z-index, 2); - } - - :host([open]) { - display: block !important; - position: fixed !important; - } - - :host(:not([open])) { - display: none; - } - - [part="close-button"] { - color: var(--rh-color-text-on-light, #151515); - background-color: transparent; - border: none; - margin: 0; - padding: var(--rh-space-md, 8px); - line-height: 0 !important; - } - - #container { - position: relative; - background-color: var(--rh-color-surface-lightest, #ffffff); - width: auto; - } - - #close-button-container { - padding-inline: var(--rh-space-md, 8px); - padding-block: var(--rh-space-lg, 16px); - max-height: var(--uxdot-masthead-max-height, 72px); - } - - #close-button:focus { - outline: var(--rh-border-width-md, 2px) solid var(--rh-color-border-interactive-on-light, #0066cc); - border-radius: var(--rh-border-radius-default, 3px); - } - - [part="overlay"] { - --_gray-90-rgb: var(--rh-color-gray-90-rgb, 31 31 31); - - display: none; - background-color: rgb(var(--_gray-90-rgb) / var(--rh-opacity-60, 60%)); - position: fixed; - top: 0; - height: 100dvh; - width: 100dvw; - z-index: -1; - } - - ::slotted(ul) { - padding-inline: 0; - padding-block-start: var(--_padding-start); - /* allow overflow to scroll with --_padding-end + section item height */ - padding-block-end: calc(var(--_padding-end) + (var(--rh-font-size-body-text-lg, 1.125rem) * 1.5) + (var(--rh-space-lg, 16px) * 2)); - list-style: none; - margin-block: 0 !important; - height: var(--_max-height); - overflow-y: scroll; - background-color: var(--rh-color-surface-lightest, #ffffff); - } - - :host([open]) [part="overlay"] { - display: block; - } - - @media (min-width: 320px) { - - :host { - --uxdot-sidenav-width: 320px; - - width: var(--uxdot-sidenav-width); - box-shadow: var(--rh-box-shadow-lg, 0 6px 8px 2px rgba(21, 21, 21, 0.3)); - } - - #container { - width: var(--uxdot-sidenav-width, 320px); - } - } - - @media (min-width: 576px) { - #close-button-container { - padding: var(--rh-space-lg, 16px); - } - } - - @media (min-width: 992px) { - :host { - --uxdot-siznav-z-index: 1; - - position: fixed; - top: var(--uxdot-masthead-max-height, 72px); - height: calc(var(--_max-height) - var(--uxdot-masthead-max-height, 72px)); - box-shadow: unset; - } - - :host(:not([open])) { - display: block; - } - - #close-button-container { - display: none; - } - - :host([open]) [part="overlay"] { - display: none; - } - } - `; - - - static isDropdown(element) { - return element instanceof UxdotSideNavDropdown; - } - - #triggerElement = null; - - #closeButton = null; - - #tabindex = RovingTabindexController.of(this, { - getItems: () => { - if (isServer) { - return []; - } - const slot = this.shadowRoot?.querySelector('slot').assignedElements({ flatten: true }) ?? []; - return slot?.flatMap(slotted => { - return Array.from(slotted.querySelectorAll(`uxdot-sidenav-dropdown > details > summary, uxdot-sidenav-item > a, details[open] uxdot-sidenav-dropdown-menu-item > a`)) ?? []; - }); - }, - }); - - async connectedCallback() { - super.connectedCallback(); - - this.#triggerElement = (this.getRootNode()).getElementById(this.trigger); - if (!isServer) { - this.#triggerElement.addEventListener('click', this.#onTriggerClick.bind(this)); - this.addEventListener('click', this.#onClick.bind(this)); - this.addEventListener('expand', this.#onExpandRequest); - this.addEventListener('keydown', this.#onKeydown.bind(this)); - window.addEventListener('keyup', this.#onKeyup.bind(this)); - } - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.#triggerElement.removeEventListener('click', this.#onTriggerClick.bind(this)); - window.removeEventListener('keyup', this.#onKeyup); - } - - render() { - return html` -
-
- -
- -
-
- `; - } - - updated() { - this.#closeButton = this.shadowRoot?.getElementById('close-button'); - } - - async toggle(trapFocus = true) { - this.open = !this.open; - await this.updateComplete; - - if (trapFocus) { - if (this.open) { - this.#closeButton?.focus(); - } else { - this.#triggerElement?.focus(); - } - } - } - - #onTriggerClick(event) { - event.preventDefault(); - this.toggle(); - } - - #onClick(event) { - const path = event.composedPath(); - if (!path.includes(this)) { - this.toggle(); - } - } - - async #onExpandRequest(event) { - if (UxdotSideNav.isDropdown(event.target)) { - const detailsOpen = event.target.querySelector('details').hasAttribute('open'); - this.#tabindex.atFocusedItemIndex = - this.#tabindex.items.indexOf(event.target.querySelector('summary')); - } - } - - #onKeydownCloseButton(event) { - switch (event.key) { - case 'Enter': - event.preventDefault(); - this.toggle(); - return; - } - } - - #onKeydown(event) { - switch (event.key) { - case 'Escape': { - if (!this.open) { - return; - } - this.toggle(); - break; - } - default: - break; - } - } - - #onKeyup(event) { - switch (event.key) { - case 'Tab': - this.#onTabKeyup(event); - break; - default: - break; - } - } - - close(trapFocus = true) { - if (!this.open) { - return; - } - this.toggle(trapFocus); - } - - #onTabKeyup(event) { - const { target } = event; - if (!this.contains(target)) { - this.close(false); - } - } -} - -/* ****************** */ -/* UXDOT-SIDENAV-ITEM */ -/* ****************** */ -class UxdotSideNavItem extends SSRRoleElement { - static styles = css` - :host { - display: block; - border-inline-start: - var(--rh-border-width-lg, 3px) - solid - transparent; - } - - :host:hover { - background: var(--rh-color-surface-lighter, #f2f2f2); - border-inline-start-color: var(--rh-color-border-subtle-on-light, #c7c7c7); - } - - :host([active]) { - background: var(--rh-color-surface-lighter, #f2f2f2); - border-inline-start-color: var(--rh-color-accent-brand-on-light, #ee0000); - } - - a { - display: block; - font-size: var(--rh-font-size-body-text-lg, 1.125rem); - padding: var(--rh-space-lg, 16px) var(--rh-space-2xl, 32px); - } - - a, - ::slotted(a) { - text-decoration: none !important; - color: var(--rh-color-text-primary-on-light, #151515) !important; - } - - a:hover { - background: var(--rh-color-surface-lighter, #f2f2f2); - border-inline-start-color: var(--rh-color-border-subtle-on-light, #c7c7c7); - } - `; - - static properties = { - active: { type: Boolean }, - href: { }, - }; - - constructor() { - super(); - this.role = 'menuitem'; - this.active = false; - } - - render() { - return html` - - `; - } -} - -/* *************************** */ -/* UXDOT-SIDENAV-DROPDOWN */ -/* *************************** */ -class UxdotSideNavDropdown extends SSRRoleElement { - static styles = css` - :host { - z-index: var(--uxdot-sidenav-z-index, 102); - } - `; - - static properties = { - expanded: { state: true }, - }; - - constructor() { - super(); - this.role = 'menu'; - this.expanded = false; - } - - connectedCallback() { - super.connectedCallback(); - if (!isServer) { - this.addEventListener('click', this.#onClick); - } - } - - render() { - return html` - - `; - } - - async #onClick(event) { - if (!event.composedPath().some(node => node instanceof HTMLAnchorElement)) { - event.preventDefault(); - this.expanded = !this.expanded; - this.querySelector('details').toggleAttribute('open', this.expanded); - // trigger change event which evokes the mutation on this.expanded - this.dispatchEvent(new CustomEvent('expand', { - bubbles: true, - composed: true, - detail: { - expanded: this.expanded, - toggle: this, - }, - })); - } - } -} - - -/* *************************** */ -/* UXDOT-SIDENAV-DROPDOWN-MENU */ -/* *************************** */ -class UxdotSideNavDropdownMenu extends SSRRoleElement { - static styles = css` - :host { - display: block; - padding-inline-start: var(--rh-space-2xl, 32px); - } - `; - - constructor() { - super(); - this.role = 'menu'; - } - - render() { - return html` - - `; - } -} - -/* ******************************** */ -/* UXDOT-SIDENAV-DROPDOWN-MENU-ITEM */ -/* ******************************** */ -class UxdotSideNavDropdownMenuItem extends SSRRoleElement { - static styles = css` - a { - display: block; - padding: var(--rh-space-md, 8px) 0 var(--rh-space-md, 8px) var(--rh-space-lg, 16px); - width: 100%; - height: 100%; - border-inline-start: - var(--rh-border-width-lg, 3px) - solid - transparent; - font-size: var(--rh-font-size-body-text-md, 1rem); - text-decoration: none; - color: var(--rh-color-text-primary-on-light, #151515) !important; - } - - :host([active]) a { - background: var(--rh-color-surface-lighter, #f2f2f2); - border-inline-start-color: var(--rh-color-accent-brand-on-light, #ee0000); - } - - a:hover { - background: var(--rh-color-surface-lighter, #f2f2f2); - border-inline-start-color: var(--rh-color-border-subtle-on-light, #c7c7c7); - } - `; - - static properties = { - active: { type: Boolean }, - href: { }, - }; - - constructor() { - super(); - this.role = 'menuitem'; - this.active = false; - } - - render() { - return html` - - `; - } -} - -customElements.define('uxdot-sidenav', UxdotSideNav); -customElements.define('uxdot-sidenav-item', UxdotSideNavItem); -customElements.define('uxdot-sidenav-dropdown', UxdotSideNavDropdown); -customElements.define('uxdot-sidenav-dropdown-menu', UxdotSideNavDropdownMenu); -customElements.define('uxdot-sidenav-dropdown-menu-item', UxdotSideNavDropdownMenuItem); diff --git a/docs/assets/javascript/elements/uxdot-sidenav.ts b/docs/assets/javascript/elements/uxdot-sidenav.ts new file mode 100644 index 0000000000..00de3fbf90 --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-sidenav.ts @@ -0,0 +1,241 @@ +import { LitElement, html, isServer } from 'lit'; + +import { classMap } from 'lit/directives/class-map.js'; +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; + +import { RovingTabindexController } from '@patternfly/pfe-core/controllers/roving-tabindex-controller.js'; + +import '@rhds/elements/rh-icon/rh-icon.js'; + +import styles from './uxdot-sidenav.css'; +import itemStyles from './uxdot-sidenav-item.css'; +import dropdownStyles from './uxdot-sidenav-dropdown.css'; +import dropdownMenuStyles from './uxdot-sidenav-dropdown-menu.css'; +import dropdownMenuItemStyles from './uxdot-sidenav-dropdown-menu-item.css'; + +@customElement('uxdot-sidenav') +export class UxdotSideNav extends LitElement { + static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }; + + static styles = [styles]; + + @property({ type: Boolean, reflect: true }) open = false; + + @property() trigger?: string; + + #triggerElement: HTMLElement | null = null; + + #closeButton: HTMLElement | null = null; + + #tabindex = RovingTabindexController.of(this, { + getItems: () => isServer ? [] : this.shadowRoot?.querySelector('slot') + ?.assignedElements({ flatten: true }) + ?.flatMap(slotted => Array.from(slotted.querySelectorAll([ + 'uxdot-sidenav-dropdown > details > summary', + 'uxdot-sidenav-item', + 'details[open] uxdot-sidenav-dropdown-menu-item', + ].join()))) ?? [], + }); + + async connectedCallback() { + super.connectedCallback(); + + const root = this.getRootNode() as Document | ShadowRoot; + if (this.trigger) { + this.#triggerElement = root.getElementById(this.trigger); + } + if (!isServer) { + this.#triggerElement?.addEventListener('click', this.#onTriggerClick.bind(this)); + this.addEventListener('click', this.#onClick.bind(this)); + this.addEventListener('expand', this.#onExpandRequest); + this.addEventListener('keydown', this.#onKeydown.bind(this)); + window.addEventListener('keyup', this.#onKeyup.bind(this)); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.#triggerElement?.removeEventListener('click', this.#onTriggerClick.bind(this)); + window.removeEventListener('keyup', this.#onKeyup); + } + + render() { + return html` +
+
+ +
+ +
+
+ `; + } + + updated() { + this.#closeButton = this.shadowRoot?.getElementById('close-button') ?? null; + } + + async toggle(trapFocus = true) { + this.open = !this.open; + await this.updateComplete; + + if (trapFocus) { + if (this.open) { + this.#closeButton?.focus(); + } else { + this.#triggerElement?.focus(); + } + } + } + + #onTriggerClick(event: Event) { + event.preventDefault(); + this.toggle(); + } + + #onClick(event: Event) { + const path = event.composedPath(); + if (!path.includes(this)) { + this.toggle(); + } + } + + async #onExpandRequest(event: Event) { + if (event.target instanceof UxdotSideNavDropdown) { + this.#tabindex.atFocusedItemIndex = + this.#tabindex.items.indexOf(event.target.querySelector('summary') as UxdotSideNavItem); + } + } + + #onKeydownCloseButton(event: KeyboardEvent) { + switch (event.key) { + case 'Enter': + event.preventDefault(); + this.toggle(); + return; + } + } + + #onKeydown(event: KeyboardEvent) { + switch (event.key) { + case 'Escape': { + if (!this.open) { + return; + } + this.toggle(); + break; + } + default: + break; + } + } + + #onKeyup(event: KeyboardEvent) { + switch (event.key) { + case 'Tab': + this.#onTabKeyup(event); + break; + default: + break; + } + } + + close(trapFocus = true) { + if (!this.open) { + return; + } + this.toggle(trapFocus); + } + + #onTabKeyup(event: KeyboardEvent) { + const { target } = event; + if (target instanceof Node && !this.contains(target)) { + this.close(false); + } + } +} + +@customElement('uxdot-sidenav-item') +export class UxdotSideNavItem extends LitElement { + static styles = [itemStyles]; + + @property({ type: Boolean, reflect: true }) active = false; + + @property() href?: string; + + @property({ reflect: true }) role = 'menuitem'; + + render() { + const { active } = this; + return html` + + `; + } +} + +@customElement('uxdot-sidenav-dropdown') +export class UxdotSideNavDropdown extends LitElement { + static styles = [dropdownStyles]; + + @property({ type: Boolean, reflect: true }) expanded = false; + + @property({ reflect: true }) role = 'menu'; + + connectedCallback() { + super.connectedCallback(); + if (!isServer) { + this.addEventListener('click', this.#onClick); + } + } + + render() { + return html` + + `; + } + + async #onClick(event: Event) { + if (!event.composedPath().some(node => node instanceof HTMLAnchorElement)) { + event.preventDefault(); + this.expanded = !this.expanded; + this.querySelector('details')?.toggleAttribute('open', this.expanded); + // trigger change event which evokes the mutation on this.expanded + this.dispatchEvent(new CustomEvent('expand', { + bubbles: true, + composed: true, + detail: { + expanded: this.expanded, + toggle: this, + }, + })); + } + } +} + +@customElement('uxdot-sidenav-dropdown-menu') +export class UxdotSideNavDropdownMenu extends LitElement { + static styles = [dropdownMenuStyles]; + + @property({ reflect: true }) role = 'menu'; + + render() { + return html` + + `; + } +} + +@customElement('uxdot-sidenav-dropdown-menu-item') +export class UxdotSideNavDropdownMenuItem extends UxdotSideNavItem { + static styles = [itemStyles, dropdownMenuItemStyles]; +} + diff --git a/docs/assets/javascript/elements/uxdot-skip-navigation.js b/docs/assets/javascript/elements/uxdot-skip-navigation.js deleted file mode 100644 index a9062c97f1..0000000000 --- a/docs/assets/javascript/elements/uxdot-skip-navigation.js +++ /dev/null @@ -1,50 +0,0 @@ -import { LitElement, html, css } from 'lit'; - -class UxdotSkipNavigation extends LitElement { - static styles = css` - :host { - display: block; - } - - ::slotted(a) { - position: absolute; - top: -40px; - left: 50%; - transform: translateX(-50%); - padding: var(--rh-space-sm, 6px); - color: var(--rh-color-text-primary-on-light, #151515); - border-inline: - var(--rh-border-width-sm, 1px) - solid - var(--rh-color-border-strong-on-light, #151515); - border-block-end: - var(--rh-border-width-sm, 1px) - solid - var(--rh-color-border-strong-on-light, #151515); - border-radius: var(--rh-border-radius-default, 3px); - background: var(--rh-color-surface-light, #e0e0e0); - transition: top 0.5s ease-out; - z-index: var(--uxdot-skip-navigation-z-index, 5); - } - - ::slotted(a:focus) { - top: 0; - outline-color: transparent; - transition: top 0.05s ease-in; - } - - @media (prefers-reduced-motion: reduce) { - ::slotted(a) { - transition-duration: 0.001ms !important; - } - } - `; - - render() { - return html` - - `; - } -} - -customElements.define('uxdot-skip-navigation', UxdotSkipNavigation); diff --git a/docs/assets/javascript/elements/uxdot-toc-lightdom.css b/docs/assets/javascript/elements/uxdot-toc-lightdom.css index c6451a2b02..7b04e989bd 100644 --- a/docs/assets/javascript/elements/uxdot-toc-lightdom.css +++ b/docs/assets/javascript/elements/uxdot-toc-lightdom.css @@ -2,15 +2,14 @@ uxdot-toc > ol { columns: 16rem auto; list-style: none; padding: 0; - margin-block-start: var(--rh-space-lg, 16px); - margin-block-end: 0; + margin-block: var(--rh-space-lg) 0; } uxdot-toc > ol > li { display: list-item; - margin-inline-start: var(--rh-space-lg, 16px); - margin-block-end: var(--rh-space-lg, 16px); - padding-inline-start: var(--rh-space-lg, 16px); + margin-inline-start: var(--rh-space-lg); + margin-block-end: var(--rh-space-lg); + padding-inline-start: var(--rh-space-lg); } uxdot-toc > ol > li:first-of-type { @@ -34,6 +33,6 @@ uxdot-toc > ol > li > a { } uxdot-toc > ol > li:first-of-type { - margin-block-start: var(--rh-space-md, 8px); + margin-block-start: var(--rh-space-md); } } diff --git a/docs/assets/javascript/elements/uxdot-toc.css b/docs/assets/javascript/elements/uxdot-toc.css new file mode 100644 index 0000000000..8539866d6b --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-toc.css @@ -0,0 +1,74 @@ +:host { + display: block; + container-type: inline-size; + container-name: host; +} + +details { + display: flex; + padding-inline: var(--rh-space-xl); + border-inline-start: var(--rh-border-width-lg) solid transparent; + border: var(--rh-border-width-sm) solid var(--rh-color-border-subtle-on-light); +} + +details[open]:after { + content: ""; + position: absolute; + inset-block: 0; + inset-inline-start: 0; + inset-block-start: -1px; + width: var(--rh-border-width-lg); + background-color: var(--rh-color-brand-red-on-light); +} + +summary { + list-style: none; + display: inline-flex; + gap: var(--rh-space-sm); + align-items: center; + padding-block: var(--rh-space-lg); +} + +summary, +#summary { + font-family: var(--rh-font-family-body-text); + font-size: var(--rh-font-size-body-text-xl) !important; + font-weight: var(--rh-font-weight-body-text-medium); +} + +summary:before { + content: ""; + width: 1rem; + height: 1rem; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 10 15' fill='none'%3E%3Cg clip-path='url(%23clip0_232_19576)'%3E%3Cpath d='M2.5 14.5L0.5 12.5L5.5 7.5L0.5 2.5L2.5 0.5L9.5 7.5L2.5 14.5Z' fill='%23707070'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_232_19576'%3E%3Crect width='10' height='14' fill='white' transform='matrix(-1 0 0 1 10 0.5)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-size: contain; + transition: 0.2s; +} + +summary::-webkit-details-marker, +summary::marker { + display: none; +} + +details[open] > summary:before { + transform: rotate(90deg); +} + +nav { + margin-block-end: var(--rh-space-lg); +} + +#expanded { + display: none; +} + +@media (min-width: 568px) { + details { + display: none; + } + + #expanded { + display: block; + } +} diff --git a/docs/assets/javascript/elements/uxdot-toc.js b/docs/assets/javascript/elements/uxdot-toc.js deleted file mode 100644 index bb510c4466..0000000000 --- a/docs/assets/javascript/elements/uxdot-toc.js +++ /dev/null @@ -1,111 +0,0 @@ -import { LitElement, html, css } from 'lit'; - -class UxdotToc extends LitElement { - static styles = css` - :host { - display: block; - container-type: inline-size; - container-name: host; - } - - details { - display: flex; - padding-inline: var(--rh-space-xl, 24px); - border-inline-start: var(--rh-border-width-lg, 3px) solid transparent; - border: var(--rh-border-width-sm, 1px) solid var(--rh-color-border-subtle-on-light, #c7c7c7); - } - - details[open]::after { - content: ""; - position: absolute; - inset-block: 0px; - inset-inline-start: 0px; - inset-block-start: -1px; - width: var(--rh-border-width-lg, 3px); - background-color: var(--rh-color-brand-red-on-light, #ee0000); - } - - summary { - list-style: none; - display: inline-flex; - gap: var(--rh-space-sm, 8px); - align-items: center; - padding-block: var(--rh-space-lg, 16px); - } - - summary, - #summary { - font-family: var(--rh-font-family-body-text, RedHatText, 'Red Hat Text', 'Noto Sans Arabic', 'Noto Sans Hebrew', 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans Malayalam', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans Thai', Helvetica, Arial, sans-serif); - font-size: var(--rh-font-size-body-text-xl, 1.25rem) !important; - font-weight: var(--rh-font-weight-body-text-medium, 500); - } - - summary::before { - content: ''; - width: 1rem; - height: 1rem; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 10 15' fill='none'%3E%3Cg clip-path='url(%23clip0_232_19576)'%3E%3Cpath d='M2.5 14.5L0.5 12.5L5.5 7.5L0.5 2.5L2.5 0.5L9.5 7.5L2.5 14.5Z' fill='%23707070'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_232_19576'%3E%3Crect width='10' height='14' fill='white' transform='matrix(-1 0 0 1 10 0.5)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-size: contain; - transition: 0.2s; - } - summary::-webkit-details-marker, - summary::marker { - display: none; - } - - details[open] > summary::before { - transform: rotate(90deg); - } - - nav { - margin-block-end: var(--rh-space-lg, 16px); - } - - #expanded { - display: none; - } - - @media (min-width: 568px) { - details { - display: none; - } - - #expanded { - display: block; - } - } - `; - - static properties = { - summary: { type: String, attribute: 'summary' }, - }; - - render() { - return html` -
-
- ${this.summary} - -
-
-
${this.summary}
- -
-
- `; - } - - _handleToggle() { - this.dispatchEvent(new CustomEvent('toggle', { - bubbles: true, - composed: true, - })); - } -} - -customElements.define('uxdot-toc', UxdotToc); diff --git a/docs/assets/javascript/elements/uxdot-toc.ts b/docs/assets/javascript/elements/uxdot-toc.ts new file mode 100644 index 0000000000..ed58f329dc --- /dev/null +++ b/docs/assets/javascript/elements/uxdot-toc.ts @@ -0,0 +1,32 @@ +import { LitElement, html } from 'lit'; + +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; + +import styles from './uxdot-toc.css'; + +@customElement('uxdot-toc') +export class UxdotToc extends LitElement { + static styles = [styles]; + + @property() summary?: string; + + render() { + return html` + + `; + } +} diff --git a/docs/foundations/color/usage.md b/docs/foundations/color/usage.md index 3b9589a8cf..c2473c2348 100644 --- a/docs/foundations/color/usage.md +++ b/docs/foundations/color/usage.md @@ -233,14 +233,14 @@ Brand team first. If you need one or more custom design tokens, contact the [Design system](https://github.com/RedHat-UX/red-hat-design-system/discussions) team first.
- + Blue submit button, blue default call to action, black tooltip, and blue switch

Use the color variants already available for elements and patterns.

- + Magenta button, brand red default call to action, green tooltip, and dark orange switch @@ -257,13 +257,13 @@ section of color or an image background with low contrast, consider using elements and patterns from the desaturated theme instead.
- + Examples of a blue button against a light gray background and a red CTA against a black background

Use a surface color token for background to ensure accessibility, or use a tool to check proper contrast.

- + Examples of a blue button against a red background and a red CTA against a blue background diff --git a/docs/foundations/typography/usage.md b/docs/foundations/typography/usage.md index d44eb5621b..505effb27e 100644 --- a/docs/foundations/typography/usage.md +++ b/docs/foundations/typography/usage.md @@ -72,13 +72,13 @@ If you are placing text by itself on a grid, a comfortable line length is betwee ### Using headings
- + Example of a heading set correctly in Red Hat Display.

Use the correct font family for the correct use case.

- + Example of a heading set incorrectly in Red Hat Text. @@ -89,13 +89,13 @@ If you are placing text by itself on a grid, a comfortable line length is betwee ### Using body text
- + Example of body text set correctly in Red Hat Text.

Use the correct font family for the correct use case.

- + Example of body text set incorrectly in Red Hat Display. @@ -106,13 +106,13 @@ If you are placing text by itself on a grid, a comfortable line length is betwee ### Spacing in between text styles
- + Example of comfortable spacing in between various text styles which is correct.

Use comfortable spacing in between text styles.

- + Example of uncomfortable spacing in between various text styles which is incorrect. @@ -123,4 +123,4 @@ If you are placing text by itself on a grid, a comfortable line length is betwee

Foundations

To learn how to use our other foundations in your designs, visit the foundations section.

-
\ No newline at end of file + diff --git a/docs/patterns/logo-wall/guidelines.md b/docs/patterns/logo-wall/guidelines.md index c93f97b33a..d21b1598f7 100644 --- a/docs/patterns/logo-wall/guidelines.md +++ b/docs/patterns/logo-wall/guidelines.md @@ -61,7 +61,7 @@ Logo sizes may decrease on smaller screens. Logo containers can adjust margins a When displaying our partners' logos, it is essential to respect their brand identity by ensuring that their logos meet accessibility standards.
- + Examples of two Red Hat logos in dark and light themes against background colors that ensure enough color contrast. @@ -69,7 +69,7 @@ When displaying our partners' logos, it is essential to respect their brand iden

Ensure that the color contrast between each logo and the background meets accessibility standards.

- + Examples of two Red Hat logos in dark and light themes against background colors that do not provide enough contrast. @@ -81,7 +81,7 @@ When displaying our partners' logos, it is essential to respect their brand iden ### Logo sizes
- + Example of a borderless logo wall with similarly sized logos that represent each partner evenly. @@ -89,7 +89,7 @@ When displaying our partners' logos, it is essential to respect their brand iden

Because the size and shape of our partners’ logos can vary, make sure to size them similarly so that each partner is evenly represented.

- + Example of a borderless logo wall in which the sizes of logos varies and disproportionately represents each partner. @@ -101,7 +101,7 @@ When displaying our partners' logos, it is essential to respect their brand iden ### Alignment
- + Example of a borderless logo wall in which each logo is horizontally and vertically aligned in its container. @@ -109,7 +109,7 @@ When displaying our partners' logos, it is essential to respect their brand iden

Align logos vertically and horizontally within a container.

- + Example of a borderless logo wall in which each logo is aligned to the bottom of the container. diff --git a/docs/styles/pages/backpage.css b/docs/styles/pages/backpage.css index f70abe6da1..45f3b9694d 100644 --- a/docs/styles/pages/backpage.css +++ b/docs/styles/pages/backpage.css @@ -55,10 +55,9 @@ max-width: 56rem; } - aside { + uxdot-toc { grid-area: aside; padding-inline: var(--rh-space-lg, 16px); - position: relative; } /* when a section is used */ @@ -122,13 +121,13 @@ } @container main (min-width: 576px) { - aside { + uxdot-toc { padding-inline: var(--rh-space-2xl, 32px); } } @container main (min-width: 768px) { - aside { + uxdot-toc { padding-inline: var(--rh-space-3xl, 48px); } } @@ -138,7 +137,7 @@ row-gap: var(--rh-space-6xl, 96px); } - aside { + uxdot-toc { padding-inline: var(--rh-space-6xl, 96px); } } @@ -163,9 +162,8 @@ "content aside"; } - aside { - padding-inline-start: 0; - padding-inline-end: var(--rh-space-2xl, 32px); + uxdot-toc { + padding-inline: 0 var(--rh-space-2xl, 32px); max-width: 320px; } } diff --git a/docs/styles/styles.css b/docs/styles/styles.css index 9dc399ad3c..6c377bc6ae 100644 --- a/docs/styles/styles.css +++ b/docs/styles/styles.css @@ -23,37 +23,37 @@ @layer base { body { - color: var(--rh-color-text-primary-on-light, #151515); + color: var(--rh-color-text-primary-on-light); } :where(p, ul, ol, dl) { - font-size: var(--rh-font-size-body-text-lg, 1.125rem); + font-size: var(--rh-font-size-body-text-lg); } :where(h1, h2, h3, h4, h5, h6) { - font-weight: var(--rh-font-weight-heading-medium, 500); + font-weight: var(--rh-font-weight-heading-medium); } a { - color: var(--rh-color-interactive-blue-darker, #0066cc); + color: var(--rh-color-interactive-blue-darker); } a:hover { - color: var(--rh-color-interactive-blue-darkest, #003366); + color: var(--rh-color-interactive-blue-darkest); } a:visited { - color: var(--rh-color-interactive-purple-darker, #5e40be); + color: var(--rh-color-interactive-purple-darker); } hr { border-block-start: - var(--rh-border-width-sm, 1px) + var(--rh-border-width-sm) solid - var(--rh-color-border-subtle-on-light, #c7c7c7); + var(--rh-color-border-subtle-on-light); border-inline: none; border-block-end: none; - margin-block-end: var(--rh-space-2xl, 32px); + margin-block-end: var(--rh-space-2xl); } figure > img { @@ -61,7 +61,7 @@ } figcaption { - color: var(--rh-color-text-secondary-on-light, #4d4d4d); + color: var(--rh-color-text-secondary-on-light); } } @@ -74,7 +74,7 @@ :target { /* Set padding for #hash links */ - scroll-margin-top: calc(var(--uxdot-masthead-max-height) + var(--rh-space-5xl, 80px)); + scroll-margin-top: calc(var(--uxdot-masthead-max-height) + var(--rh-space-5xl)); } body { @@ -92,18 +92,15 @@ main { grid-area: main; - border-inline-start: - var(--rh-border-width-sm, 1px) - solid - var(--rh-color-border-subtle-on-light, #c7c7c7); + border-inline-start: var(--rh-border-width-sm) solid var(--rh-color-border-subtle-on-light); container-type: inline-size; container-name: main; padding-block-start: var(--uxdot-masthead-max-height); } .container { - padding-inline: var(--rh-space-lg, 16px); - padding-block-end: var(--rh-space-lg, 16px); + padding-inline: var(--rh-space-lg); + padding-block-end: var(--rh-space-lg); } uxdot-masthead { @@ -139,50 +136,48 @@ @container main (min-width: 576px) { .container { - padding-inline: var(--rh-space-2xl, 32px); - padding-block-end: var(--rh-space-2xl, 32px); + padding-inline: var(--rh-space-2xl); + padding-block-end: var(--rh-space-2xl); } } @container main (min-width: 768px) { .container { - padding-inline: var(--rh-space-3xl, 48px); - padding-block-end: var(--rh-space-3xl, 48px); + padding-inline: var(--rh-space-3xl); + padding-block-end: var(--rh-space-3xl); } } @container main (min-width: 992px) { .container { max-width: var(--container-max-width); - padding-inline: var(--rh-space-6xl, 96px); - padding-block-start: 0; - padding-block-end: var(--rh-space-6xl, 96px); + padding-inline: var(--rh-space-6xl); + padding-block: 0 var(--rh-space-6xl); } } } @layer flow { :where(p, ul, ol, dl) { - margin-block-start: var(--rh-space-lg, 16px); - margin-block-end: var(--rh-space-2xl, 32px); + margin-block: var(--rh-space-lg) var(--rh-space-2xl); } :where(li) { - margin-block: var(--rh-space-md, 8px); + margin-block: var(--rh-space-md); } :where(h1, h2, h3, h4, h5, h6) { - margin-block-end: var(--rh-space-lg, 16px); + margin-block-end: var(--rh-space-lg); } :where(h1, h2, h3, h4, h5, h6) + :where(h2, h3, h4, h5, h6), /* any where copy-permalink is used as a sibling to another */ :where(uxdot-copy-permalink + uxdot-copy-permalink) { - margin-block-start: var(--rh-space-2xl, 32px); + margin-block-start: var(--rh-space-2xl); } :where(uxdot-copy-permalink, h1, h2, h3 ,h4, h5 ,h6) + uxdot-example { - margin-block-start: var(--rh-space-lg, 16px); + margin-block-start: var(--rh-space-lg); } figure uxdot-example { @@ -190,15 +185,15 @@ } rh-alert + uxdot-example { - margin-block-start: var(--rh-space-2xl, 32px); + margin-block-start: var(--rh-space-2xl); } rh-alert + .grid { - margin-block-start: var(--rh-space-2xl, 32px); + margin-block-start: var(--rh-space-2xl); } rh-table { - margin-block: var(--rh-space-3xl, 48px); + margin-block: var(--rh-space-3xl); } /* TODO: look into this do we need this rule? */ @@ -207,12 +202,12 @@ } rh-alert + rh-alert { - margin-block-start: var(--rh-space-xl, 24px); + margin-block-start: var(--rh-space-xl); } uxdot-example + figcaption, figcaption + uxdot-example { - margin-block: var(--rh-space-xl, 24px); + margin-block: var(--rh-space-xl); } } @@ -226,11 +221,11 @@ **/ :where(code) { - padding-inline: var(--rh-space-xs, 4px); - background: var(--rh-color-surface-light, #e0e0e0); - border-radius: var(--rh-border-radius-default, 3px); - font-size: var(--rh-font-size-code-md, 1rem); - font-weight: var(--rh-font-weight-code-regular, 400); + padding-inline: var(--rh-space-xs); + background: var(--rh-color-surface-light); + border-radius: var(--rh-border-radius-default); + font-size: var(--rh-font-size-code-md); + font-weight: var(--rh-font-weight-code-regular); } :where(code:empty) { @@ -243,20 +238,20 @@ } :where(kbd) { - --_uxdot-kbd-color-border: var(--rh-color-border-subtle-on-light, #c7c7c7); + --_uxdot-kbd-color-border: var(--rh-color-border-subtle-on-light); - background-color: var(--rh-color-surface-lightest, #ffffff); - border-radius: var(--rh-border-radius-default, 3px); - border: var(--rh-border-width-sm, 1px) solid var(--_uxdot-kbd-color-border); + background-color: var(--rh-color-surface-lightest); + border-radius: var(--rh-border-radius-default); + border: var(--rh-border-width-sm) solid var(--_uxdot-kbd-color-border); box-shadow: 0 2px 0 1px var(--_uxdot-kbd-color-border); cursor: default; - font-size: var(--rh-font-size-body-text-md, 1rem); + font-size: var(--rh-font-size-body-text-md); line-height: 1; min-width: 0.75rem; display: inline-block; text-align: center; - padding: 2px var(--rh-space-sm, 6px); - margin-inline: var(--rh-space-xs, 4px); + padding: 2px var(--rh-space-sm); + margin-inline: var(--rh-space-xs); position: relative; top: -1px; } @@ -292,16 +287,16 @@ * Sticky table of contents pattern w/o subnav * progressive enhancement sticky, using :has() **/ - uxdot-header + aside > uxdot-toc { + uxdot-header + uxdot-toc { position: sticky; /* masthead height (72px) + padding (32px) */ - top: calc(var(--uxdot-masthead-max-height, 72px) + var(--rh-space-2xl, 32px)); + top: calc(var(--uxdot-masthead-max-height) + var(--rh-space-2xl)); } - uxdot-header:has(rh-subnav) + aside > uxdot-toc { + uxdot-header:has(rh-subnav) + uxdot-toc { /* masthead height (72px) + subnav height 56px + padding (32px) */ - top: calc(var(--uxdot-masthead-max-height, 72px) + 56px + var(--rh-space-2xl, 32px)); + top: calc(var(--uxdot-masthead-max-height) + 56px + var(--rh-space-2xl)); } @container main (min-width: 768px) { @@ -335,6 +330,6 @@ rh-alert p { /* TODO: Document this bug shouldn't have to force the font size the component is trying to set internally on #description */ - font-size: var(--rh-font-size-body-text-md, 1rem) !important; + font-size: var(--rh-font-size-body-text-md) !important; } } diff --git a/docs/tokens/tokens.html b/docs/tokens/tokens.html index 9ec7a92491..b035bb5699 100644 --- a/docs/tokens/tokens.html +++ b/docs/tokens/tokens.html @@ -14,10 +14,9 @@ - rh-table --- - - - - + + + - - - - - - -
-
- -
- -
- -
- -
-
-
-
-
- -
- -
- -
- -
-
-
-
- -
- -
+ return html` + `; - - // wrap footer content in footer element if none already exists - return footer ? footerContent : html`
- ${footerContent} -
`; } } diff --git a/elements/rh-health-index/docs/20-guidelines.md b/elements/rh-health-index/docs/20-guidelines.md index f43e198826..84e275984c 100644 --- a/elements/rh-health-index/docs/20-guidelines.md +++ b/elements/rh-health-index/docs/20-guidelines.md @@ -109,14 +109,14 @@ Health index can also be inserted inline with text and components. In such cases Letter grades and severity level colors are designed to work together. Mixing them up will cause users to be confused as to what the health of something actually is.
- + Small, default, and large size health index components displaying correct letter grades and severity colors.

Keep letter grades and severity level colors consistent.

- + Small, default, and large size health index components displaying incorrect letter grades and severity colors. @@ -130,14 +130,14 @@ Letter grades and severity level colors are designed to work together. Mixing th Light theme components are designed only to work in the light theme, and dark theme components are designed to work only in the dark theme.
- + Dark theme small, default, and large size health index components.

Use the correct components in the correct theme.

- + Light theme small, default, and large size health index components used incorrectly in the dark theme. @@ -151,14 +151,14 @@ Light theme components are designed only to work in the light theme, and dark th Each variant is unique and designed to meet a specific user need.
- + Small, default, and large size health index components displaying correct letter grades and severity colors.

Use the available variants as intended.

- + Large size health index component displaying extra text and an incorrect combination of styles. diff --git a/elements/rh-icon/docs/20-guidelines.md b/elements/rh-icon/docs/20-guidelines.md index 8b28cdd77e..335e3ccec5 100644 --- a/elements/rh-icon/docs/20-guidelines.md +++ b/elements/rh-icon/docs/20-guidelines.md @@ -55,14 +55,14 @@ The association between an icon and a certain meaning can be strengthened by how Icons are pictograms and are not meant to be used at large sizes. If you need a visual element that is larger than 100px, use an [illustration](https://www.redhat.com/en/about/brand/standards/illustration) instead.
- + Image of icon at correct size

Use icons at 100px or smaller.

- + Image of icon at incorrect size @@ -75,14 +75,14 @@ Icons are pictograms and are not meant to be used at large sizes. If you need a If icons are being used within elements that are grouped together like in a vertical list of links, they should appear to have the same size.
- + Image of icon at correct consistency in sizing

Use <rh-icon> to ensure consistency and alignment.

- + Image of icon at incorrect consistency in sizing @@ -95,14 +95,14 @@ If icons are being used within elements that are grouped together like in a vert Use an icon that is familiar, can be commonly understood (ideally across cultures), and makes sense when paired with text.
- + Image of pencil icon representing familiar meaning on a edit avatar link

A pencil icon is often used to represent the ability to edit something.

- + Image of utensils icon representing unfamiliar meaning on an edit avatar link @@ -115,14 +115,14 @@ Use an icon that is familiar, can be commonly understood (ideally across culture If the meaning of two icons might be related, use the icon that could typically be seen more often. This helps users quickly recognize what interactions are possible.
- + Image of x icon representing familiar meaning of a close action

An X icon is very commonly used to indicate that something can be closed.

- + Image of trash can icon representing unfamiliar meaning of a close action diff --git a/elements/rh-icon/docs/40-accessibility.md b/elements/rh-icon/docs/40-accessibility.md index 01d3dcd949..f389e5b229 100644 --- a/elements/rh-icon/docs/40-accessibility.md +++ b/elements/rh-icon/docs/40-accessibility.md @@ -29,15 +29,15 @@ However, when an icon is accompanied by text, the icon does not need to use acce An <rh-icon> is hidden from assistive technology by default. If an icon is presentational, there’s no need to change the markup to hide the icon.
- - - - -

Icons will be hidden from assistive technology by default

+ + + +

Icons will be hidden from assistive technology by default

- - - + +

There is no need to add the additional aria-hidden="true"

@@ -52,4 +52,4 @@ An <rh-icon> is hidden from assistive technology by default. {% include 'partials/accessibility/wcag.md' %} {% include 'partials/accessibility/1.1.1-A.md' %} {% include 'partials/accessibility/1.4.1-A.md' %} -{% include 'partials/accessibility/2.5.8-AA.md' %} \ No newline at end of file +{% include 'partials/accessibility/2.5.8-AA.md' %} diff --git a/elements/rh-icon/rh-icon.ts b/elements/rh-icon/rh-icon.ts index 2078c1c9a0..d0ceb283ac 100644 --- a/elements/rh-icon/rh-icon.ts +++ b/elements/rh-icon/rh-icon.ts @@ -114,9 +114,8 @@ export class RhIcon extends LitElement { return html`
${!isServer ? content - : unsafeHTML(content as unknown as string)}${content ? '' : html` - `} + class="${classMap({ [set]: set })}">${!isServer ? content + : unsafeHTML(content as unknown as string)}
`; } @@ -130,13 +129,6 @@ export class RhIcon extends LitElement { } } - updated() { - // terrible workaround for apparent lit bug: icons render twice, once for - // ssr, then again for client-side. - // updated() is not called on server - this.shadowRoot?.querySelector('.isServer')?.remove(); - } - disconnectedCallback(): void { super.disconnectedCallback(); RhIcon.io.unobserve(this); diff --git a/elements/rh-pagination/docs/20-guidelines.md b/elements/rh-pagination/docs/20-guidelines.md index b90d7dee73..ab8d9d7915 100644 --- a/elements/rh-pagination/docs/20-guidelines.md +++ b/elements/rh-pagination/docs/20-guidelines.md @@ -142,13 +142,13 @@ Container widths of 375px - 768px ### No input field
- + Size SM truncated pagination with input below pages.

The page input field needs to be visible when there is truncation or when only the navigation controls are available.

- + Size SM truncated pagination without an input. @@ -159,13 +159,13 @@ Container widths of 375px - 768px ### Truncation
- + Size SM truncated pagination with eight pages.

Truncation occurs automatically when there are more than seven pages.

- + Pagination that is truncating only four pages is incorrect usage @@ -177,13 +177,13 @@ Container widths of 375px - 768px ### Order or alignment
- + Pagination with input field center aligned below pagination

Use the built-in alignment options for the page input field. If your language is read right to left, view the “Right to left” pagination demo.

- + Truncated pagination with the input field on top and right aligned. @@ -195,13 +195,13 @@ Container widths of 375px - 768px ### Compact vs. mobile
- + Pagination shown on a mobile device with arrows on top and an input field below.

Use the default mobile style that comes with each variant.

- + Pagination shown on a mobile device with input field in between arrows. diff --git a/elements/rh-site-status/docs/20-guidelines.md b/elements/rh-site-status/docs/20-guidelines.md index a5df89177b..d160ded1d1 100644 --- a/elements/rh-site-status/docs/20-guidelines.md +++ b/elements/rh-site-status/docs/20-guidelines.md @@ -110,14 +110,14 @@ To avoid confusion, write link text with the same severity as the status icon. Always include a status icon, it helps communicate severity visually.
- + Image of a site status element with a correct usage of a green checkmark icon and the text 'All systems operational'.

Include an icon to make it easier for users to understand.

- + Image of a site status element with an incorrect usage of an orange red exclamation point icon and the text 'All systems operational'. diff --git a/elements/rh-skip-link/docs/20-guidelines.md b/elements/rh-skip-link/docs/20-guidelines.md index 165d84f4cb..e0b93087df 100644 --- a/elements/rh-skip-link/docs/20-guidelines.md +++ b/elements/rh-skip-link/docs/20-guidelines.md @@ -41,14 +41,14 @@ The recommended maximum character count is listed below and includes spaces. The vast majority of pages will need only one skip link, unless it is a very complex page with many repeated elements. The purpose of a skip link is to help users navigate a page more quickly and easily. Adding too many skip links detracts from this goal. If you’d like users to be able to jump from one section to the other, consider using jump links instead.
- + Wireframe of a webpage with one skip link at the top

Use a skip link to help users avoid tabbing through multiple navigation items.

- + Wireframe of a webpage with skip links at the top of the page and at the top of every tile @@ -61,14 +61,14 @@ The vast majority of pages will need only one skip link, unless it is a very com “Main content” in a skip link’s text label refers to the content that appears immediately after the navigation. Ensure that users skip only the navigation, or adjust the element's text to give a more accurate description of what will be skipped.
- + Wireframe of a webpage with arrow pointing from skip link to the first interactive element on the page

Ensure that users skip only the navigation, or adjust the text of the skip link to give a more accurate description.

- + Wireframe of a webpage with arrow pointing from skip link to the second interactive element on the page @@ -81,14 +81,14 @@ The vast majority of pages will need only one skip link, unless it is a very com To prevent the skip link from being visually distracting, hide a skip link until users navigate to it. The most accessible way to hide it is to position the skip link outside of the viewport, rather than using `display: none` or the `hidden` attribute in CSS.
- + Wireframe of webpage that has a tile in focus and no visible skip link

Hide the skip link by default, until the user navigates to it.

- + Wireframe of a webpage that has a tile in focus and a visible skip link diff --git a/elements/rh-surface/docs/20-guidelines.md b/elements/rh-surface/docs/20-guidelines.md index c16ccf0a33..5ad23e7557 100644 --- a/elements/rh-surface/docs/20-guidelines.md +++ b/elements/rh-surface/docs/20-guidelines.md @@ -32,7 +32,7 @@ Surface elements are used only when another container like card or blockquote wo Avoid using surface inside of other containers. Use the appropriate `color-palette` on the container instead
- +

⚠️ Avoid

@@ -40,7 +40,7 @@ Avoid using surface inside of other containers. Use the appropriate `color-palet
- +

✅ Good

diff --git a/elements/rh-switch/docs/20-guidelines.md b/elements/rh-switch/docs/20-guidelines.md index 7f4e5758c3..9a6a0a8db1 100644 --- a/elements/rh-switch/docs/20-guidelines.md +++ b/elements/rh-switch/docs/20-guidelines.md @@ -125,14 +125,14 @@ The status message and form label should be short and direct, not neutral or amb
- + Magenta button, brand red default call to action, green tooltip, and dark orange switch

Ensure the message is clear when a Switch is toggled to the On or Off position and that the form label explains the switch's purpose.

- + Magenta button, brand red default call to action, green tooltip, and dark orange switch @@ -194,14 +194,14 @@ A Switch is successfully toggled when the handle slides to the other side of the To avoid confusion as to what a Switch will do, always include some kind of status message or a form label.
- + Switches with Bluetooth as a form label and/or a status message

Ensure the message is clear when a Switch is toggled to the On or Off position and that the form label explains the switch's purpose.

- + Two switches without a form label or status message @@ -237,14 +237,14 @@ A Switch controls binary options, not opposing ones. A binary option represents Switches can be used in a list to toggle multiple independent options.
- + Four switches stacked vertically with half of them turned on

Use Switches in a list only if the effects from toggling each Switch are immediate.

- + Four switches stacked vertically with a save button below them diff --git a/elements/rh-video-embed/docs/20-guidelines.md b/elements/rh-video-embed/docs/20-guidelines.md index caa5686793..146034e964 100644 --- a/elements/rh-video-embed/docs/20-guidelines.md +++ b/elements/rh-video-embed/docs/20-guidelines.md @@ -54,14 +54,14 @@ The video’s width will dynamically adjust with its parent container. The video Display the play button in a consistent, predictable spot for all videos.
- + Video with vertically and horizontally centered play button

Keep the play button centered horizontally and vertically.

- + Video with play button in the bottom right @@ -75,14 +75,14 @@ Display the play button in a consistent, predictable spot for all videos. A video embed element and should have the same aspect ratio as the video it’s displaying.
- + Video embed with a correct, 16:9 aspect ratio

The video embed element should display a video using its original aspect ratio.

- + Video embed with a very narrow aspect ratio diff --git a/eleventy.config.cjs b/eleventy.config.cjs index eec2ce48e1..64464613ec 100644 --- a/eleventy.config.cjs +++ b/eleventy.config.cjs @@ -13,6 +13,9 @@ const RHDSMarkdownItPlugin = require('./docs/_plugins/markdown-it.cjs'); const ImportMapPlugin = require('./docs/_plugins/importMap.cjs'); const LitPlugin = require('@lit-labs/eleventy-plugin-lit'); +const util = require('node:util'); +const exec = util.promisify(require('node:child_process').exec); + const isWatch = process.argv.includes('--serve') || process.argv.includes('--watch'); @@ -22,6 +25,14 @@ const isLocal = !(process.env.CI || process.env.DEPLOY_URL); module.exports = function(eleventyConfig) { eleventyConfig.setQuietMode(true); + eleventyConfig.on('eleventy.before', function({ runMode }) { + eleventyConfig.addGlobalData('runMode', runMode); + }); + + eleventyConfig.on('eleventy.before', async function() { + await exec('npx tspc'); + }); + eleventyConfig.addWatchTarget('docs/patterns/**/*.html'); eleventyConfig.watchIgnores?.add('docs/assets/redhat/'); eleventyConfig.watchIgnores?.add('**/*.spec.ts'); @@ -49,10 +60,6 @@ module.exports = function(eleventyConfig) { eleventyConfig.addGlobalData('isLocal', isLocal); - eleventyConfig.on('eleventy.before', function({ runMode }) { - eleventyConfig.addGlobalData('runMode', runMode); - }); - eleventyConfig.addGlobalData('sideNavDropdowns', [ { 'title': 'About', 'url': '/about', 'collection': 'about' }, { 'title': 'Get started', 'url': '/get-started', 'collection': 'getstarted' }, @@ -83,6 +90,11 @@ module.exports = function(eleventyConfig) { nodemodulesPublicPath: '/assets/packages', manualImportMap: { imports: { + 'lit/': '/assets/packages/lit/', + 'lit-html': '/assets/packages/lit-html/lit-html.js', + 'lit-html/': '/assets/packages/lit-html/', + '@lit-labs/ssr-client/lit-element-hydrate-support.js': + '/assets/packages/@lit-labs/ssr-client/lit-element-hydrate-support.js', '@rhds/tokens': '/assets/packages/@rhds/tokens/js/tokens.js', '@rhds/tokens/': '/assets/packages/@rhds/tokens/js/', '@rhds/elements/lib/': '/assets/packages/@rhds/elements/lib/', @@ -118,12 +130,14 @@ module.exports = function(eleventyConfig) { '@floating-ui/core', '@floating-ui/dom', '@lit-labs/ssr-client/', - '@lit-labs/ssr-client/lit-element-hydrate-support.js', '@lit/context', '@lit/reactive-element', '@webcomponents/template-shadowroot/template-shadowroot.js', 'lit', 'lit-element', + 'lit-html', + 'lit/decorators/custom-element.js', + 'lit/decorators/property.js', 'lit/directives/class-map.js', 'lit/directives/if-defined.js', 'lit/directives/repeat.js', @@ -171,21 +185,22 @@ module.exports = function(eleventyConfig) { eleventyConfig.addPlugin(LitPlugin, { mode: 'worker', componentModules: [ - 'docs/assets/javascript/elements/uxdot-skip-navigation.js', 'docs/assets/javascript/elements/uxdot-masthead.js', 'docs/assets/javascript/elements/uxdot-header.js', 'docs/assets/javascript/elements/uxdot-sidenav.js', 'docs/assets/javascript/elements/uxdot-hero.js', 'docs/assets/javascript/elements/uxdot-feedback.js', - 'docs/assets/javascript/elements/uxdot-feedback.js', 'docs/assets/javascript/elements/uxdot-copy-permalink.js', 'docs/assets/javascript/elements/uxdot-copy-button.js', 'docs/assets/javascript/elements/uxdot-repo-status-list.js', 'docs/assets/javascript/elements/uxdot-best-practice.js', 'docs/assets/javascript/elements/uxdot-search.js', 'docs/assets/javascript/elements/uxdot-toc.js', + 'elements/rh-button/rh-button.js', 'elements/rh-tag/rh-tag.js', 'elements/rh-icon/rh-icon.js', + 'elements/rh-skip-link/rh-skip-link.js', + 'elements/rh-footer/rh-footer-universal.js', // 'docs/assets/javascript/elements/uxdot-pattern.js', // 'docs/assets/javascript/elements/uxdot-example.js', // Uses context API need to work around issues // 'docs/assets/javascript/elements/uxdot-installation-tabs.js', // extends RhTabs so cant DSD yet diff --git a/eslint.config.js b/eslint.config.js index adc1ff4d09..69306b79bf 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -26,6 +26,7 @@ export default tseslint.config( 'docs/core', 'docs/components', 'docs/assets/playgrounds', + 'docs/assets/elements/javascript/**/*.js', 'node_modules', '!core/*/demo/*.js', diff --git a/package-lock.json b/package-lock.json index 2d6ddac1eb..dc65a78ad8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "git-branch": "^2.0.1", "image-size": "^1.1.1", "leasot": "^14.4.0", + "lit-html": "^3.2.0", "markdown-it-attrs": "^4.2.0", "markdownlint-cli2": "^0.13.0", "parse5": "^7.1.2", @@ -17537,7 +17538,8 @@ }, "node_modules/lit-html": { "version": "3.2.0", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.0.tgz", + "integrity": "sha512-pwT/HwoxqI9FggTrYVarkBKFN9MlTUpLrDHubTmW4SrkL3kkqW5gxwbxMMUnbbRHBC0WTZnYHcjDSCM559VyfA==", "dependencies": { "@types/trusted-types": "^2.0.2" } diff --git a/package.json b/package.json index 6a15bd6e74..67c789b92f 100644 --- a/package.json +++ b/package.json @@ -86,8 +86,8 @@ "service": true, "command": "web-dev-server --open --watch", "dependencies": [ - "compile", - "watch:compile", + "analyze", + "playgrounds", "watch:docs" ] }, @@ -95,9 +95,7 @@ "service": true, "command": "web-dev-server --watch", "dependencies": [ - "analyze", - "compile", - "watch:compile" + "analyze" ] }, "serve": { @@ -105,18 +103,16 @@ "command": "eleventy --serve", "dependencies": [ "analyze", - "playgrounds", - "compile", - "watch:compile" + "playgrounds" ] }, "build": { "dependencies": [ "patch", + "compile", "analyze", "react-wrappers", "entrypoint", - "compile", "copy-css", "docs" ] @@ -138,6 +134,8 @@ "command": "tspc", "files": [ "elements/**/*.ts", + "docs/assets/javascript/**/*.ts", + "docs/assets/javascript/**/*.css", "lib/**/*.css", "lib/**/*.ts" ], @@ -146,6 +144,8 @@ "elements/**/*.{d.ts,js,map}", "lib/**/*.{d.ts,js,map}", "elements/rh-secondary-nav/test/fixtures.js", + "docs/assets/javascript/**/*.d.ts", + "docs/assets/javascript/**/*.js.map", "!elements/*/{demo,test}/**/*.js" ] }, @@ -325,6 +325,7 @@ "git-branch": "^2.0.1", "image-size": "^1.1.1", "leasot": "^14.4.0", + "lit-html": "^3.2.0", "markdown-it-attrs": "^4.2.0", "markdownlint-cli2": "^0.13.0", "parse5": "^7.1.2", diff --git a/tsconfig.json b/tsconfig.json index 87d10abd27..113bd13a7c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "include": [ "./elements", + "./docs/assets/javascript/elements", "./lib", "./declaration.d.ts" ],
Ex.Use case
${token.path.includes('text') ? 'Aa' : docs?.example ?? ''} - --${token.name}-rgb + --${token.name}-rgb rgb(${r}, ${g}, ${b}) To modify opacity
${token.path.includes('text') ? 'Aa' : docs?.example ?? ''} - --${token.name}-hsl + --${token.name}-hsl hsl(${h} ${s}% ${l}%)hsl(${h} ${s}% ${l}%) To modify opacity
- - - - - +