diff --git a/.changeset/calm-cards-roll.md b/.changeset/calm-cards-roll.md new file mode 100644 index 0000000000..117683cf12 --- /dev/null +++ b/.changeset/calm-cards-roll.md @@ -0,0 +1,33 @@ +--- +"@patternfly/elements": major +--- + +``: replaces `` with PatternFly v6 design specs. + +```html + +

First card

+ Subtitle + This card is selectable. + Footer +
+``` + +**Breaking Changes from v5** + +- Renamed tag from `` to `` +- CSS custom properties renamed from `--pf-v5-c-card--*` to `--pf-v6-c-card--*` +- Removed `flat` and `rounded` attributes. v6 cards are rounded and bordered by default; use `plain` to remove the background and border. +- The shadow DOM no longer renders an `
` landmark. Cards are used in multiples, so the host is the card container to avoid landmark pollution. +- `size` remains `compact` / `large`. + +**New features** + +- `selectable`, `selection`, `selected`, `hide-input`, and `name` for checkbox and radio cards, with form association (`value` submits with the form) +- `clickable`, `href`, and `target` for full-card buttons and links +- `expandable` and `expanded` with a header toggle and cancelable `expand` / `collapse` events +- `variant="secondary"`, `glass`, and `header-wrap` modifiers +- `header`, `title`, `subtitle`, `actions`, and `expandable-content` slots +- `no-offset` and `toggle-right` header options +- `pf-v6-card-body` element for multiple body sections and `no-fill` +- `change` event for selectable cards; `header`, `actions`, `body`, `expandable-content`, `footer`, and `toggle` CSS parts diff --git a/brand/styles.css b/brand/styles.css index bea8eab853..4962cd71f3 100644 --- a/brand/styles.css +++ b/brand/styles.css @@ -17,13 +17,13 @@ h1 { height: 36px; } -pf-card .img-link { +pf-v6-card .img-link { display: flex; align-items: center; justify-content: center; } -pf-card h2 { +pf-v6-card h2 { margin-bottom: 0; padding-bottom: .5rem; margin-top: 0; @@ -44,17 +44,17 @@ pf-card h2 { margin-bottom: 0; } -pf-card h2, -pf-card h3 { +pf-v6-card h2, +pf-v6-card h3 { margin-top: 8px; } -pf-card h3 { +pf-v6-card h3 { font-weight: 700; margin-bottom: 16px; } -pf-card .cta { +pf-v6-card .cta { min-width: 100%; } diff --git a/docs/_includes/layout-blog-index.njk b/docs/_includes/layout-blog-index.njk index d6a1305f35..362d246a1b 100644 --- a/docs/_includes/layout-blog-index.njk +++ b/docs/_includes/layout-blog-index.njk @@ -9,7 +9,7 @@ layout: layout-base.njk
{% for post in collections.blog | reverse %} - +

{{ post.data.title }}

{% if post.data.tagline %}

{{ post.data.tagline }}

{% endif %} @@ -18,7 +18,7 @@ layout: layout-base.njk {% endif %} Read Post -
+ {% endfor %}
@@ -34,7 +34,8 @@ layout: layout-base.njk gap: 2rem; } - pf-v5-card::part(footer) { + pf-v6-card::part(footer) { + display: flex; justify-content: space-between; align-items: center; } diff --git a/docs/_snippets/card-html.md b/docs/_snippets/card-html.md index ad6fe1fedb..4a17be57bb 100644 --- a/docs/_snippets/card-html.md +++ b/docs/_snippets/card-html.md @@ -1,12 +1,12 @@ ```html - + - +

Card component

PatternFly Elements are custom HTML elements that work everywhere. The Card element has header and footer slots for things like titles and actions.

- More about pf-v5-card -
+ More about pf-v6-card + ``` diff --git a/docs/docs/develop/import-maps.md b/docs/docs/develop/import-maps.md index 48759e584d..a7d9738e45 100644 --- a/docs/docs/develop/import-maps.md +++ b/docs/docs/develop/import-maps.md @@ -31,14 +31,14 @@ Example: ``` @@ -65,7 +65,7 @@ Once you've generated your import map and added it to your project, you can use ``` diff --git a/docs/docs/develop/setup.md b/docs/docs/develop/setup.md index f16330be1a..ab1db52ac4 100644 --- a/docs/docs/develop/setup.md +++ b/docs/docs/develop/setup.md @@ -107,7 +107,7 @@ npm run new -- --tagName pf-v5-cool-element Running that command launches the demo app in a new browser tab, and refreshes the page on save. From there you can navigate to the demo page of the element you're working on. - For example, if you want to preview the `pf-v5-card` component, then navigate in the browser to `http://localhost:8000/demo/pf-v5-card/`. + For example, if you want to preview the `pf-v6-card` component, then navigate in the browser to `http://localhost:8000/components/v6-card/demo/`. {% endband %} {% band header="Testing" %} diff --git a/docs/framework-integration/index.md b/docs/framework-integration/index.md index f705f12b46..ca82401659 100644 --- a/docs/framework-integration/index.md +++ b/docs/framework-integration/index.md @@ -24,7 +24,7 @@ title: Framework integration
- +

Using PatternFly Elements in your React App @@ -32,8 +32,8 @@ title: Framework integration

To get web components to work with React it’s pretty easy and straightforward. If you’d like to follow along, go ahead and...

Read on Medium - - + +

Using PatternFly Elements in your Vue App @@ -41,8 +41,8 @@ title: Framework integration

To get web components to work with Vue, it’s pretty easy and straightforward. If you’d like to follow along, go ahead and...

Read on Medium - - + +

Using PatternFly Elements in your Angular App @@ -50,6 +50,6 @@ title: Framework integration

To get web components to work with Angular, there are a few steps that we need to take. If you’d like to follow along, go ahead and...

Read on Medium - +

diff --git a/docs/framework-integration/react.md b/docs/framework-integration/react.md index 52b146f474..f42af45e42 100644 --- a/docs/framework-integration/react.md +++ b/docs/framework-integration/react.md @@ -129,7 +129,7 @@ tags: import { useState } from "react"; import { Button } from "@patternfly/elements/react/pf-v5-button/pf-v5-button.js"; - import { Card } from "@patternfly/elements/react/pf-v5-card/pf-v5-card.js"; + import { Card } from "@patternfly/elements/react/pf-v6-card/pf-v6-card.js"; import { Switch } from "@patternfly/elements/react/pf-v6-switch/pf-v6-switch.js"; import { Popover } from "@patternfly/elements/react/pf-v5-popover/pf-v5-popover.js"; import { Tooltip } from "@patternfly/elements/react/pf-v6-tooltip/pf-v6-tooltip.js"; @@ -137,7 +137,7 @@ tags: import "./App.css"; ``` - Let’s use [`pf-v5-button`][pf-v5-button] and [`pf-v5-card`][pf-v5-card] component in the + Let’s use [`pf-v5-button`][pf-v5-button] and [`pf-v6-card`][pf-v6-card] component in the `App` function in the `App.tsx` file to see that our Card and Button are working. We are updating the local state and showing it in the UI after clicking the button. @@ -354,7 +354,7 @@ tags: [react-lifecycle]: https://legacy.reactjs.org/docs/state-and-lifecycle.html [vite]: https://vitejs.dev/guide/#scaffolding-your-first-vite-project [pf-v5-button]: https://patternflyelements.org/components/button/ -[pf-v5-card]: https://patternflyelements.org/components/card/ +[pf-v6-card]: https://patternflyelements.org/components/card/ [pf-v6-switch]: https://patternflyelements.org/components/switch/ [pf-v6-tooltip]: https://patternflyelements.org/components/tooltip/ [pf-v5-popover]: https://patternflyelements.org/components/popover/ diff --git a/docs/framework-integration/vue.md b/docs/framework-integration/vue.md index 6f1008b623..805c4583d4 100644 --- a/docs/framework-integration/vue.md +++ b/docs/framework-integration/vue.md @@ -41,7 +41,7 @@ tags: {% band header="Adding PatternFly Elements" %} With the setup complete, let’s add a couple of PatternFly Elements web components to our application to make sure everything is hooked up properly. - We’re going to add a card ([pf-v5-card](/components/card)). Later, we’ll add an + We’re going to add a card ([pf-v6-card](/components/card)). Later, we’ll add an accordion ([pf-v5-accordion](/components/accordion)) and some CSS to help with our layout. @@ -60,7 +60,7 @@ tags: ```html ``` @@ -65,18 +65,18 @@ title: Get started When you have the import map script loaded on the page, you can add a [card component](/components/card) using html. ```html - +

Card header

-

This is the pf-v5-card body.

+

This is the pf-v6-card body.

OK -
+ ``` - +

Card header

-

This is the pf-v5-card body.

+

This is the pf-v6-card body.

OK -
+ ### Importmap and Markup @@ -91,18 +91,18 @@ Altogether your import map code could look something like this [Lit Playground D component to see which attributes are available. ```html - +

Card header

-

This is the pf-v5-card body.

+

This is the pf-v6-card body.

OK -
+ ``` - +

Card header

-

This is the pf-v5-card body.

+

This is the pf-v6-card body.

OK -
+ {% endband %} {% band header="Use CSS variables to customize or theme your components" %} @@ -116,14 +116,14 @@ Altogether your import map code could look something like this [Lit Playground D ```css /* your-page.css */ :root { - --pf-v5-c-card--BackgroundColor: var(--pf-global--active-color--200, #bee1f4); + --pf-v6-c-card--BackgroundColor: var(--pf-t--global--color--brand--100, #bee1f4); } ``` - +

Card header

-

This is the pf-v5-card body.

- OK -
+

This is the pf-v6-card body.

+ + {% endband %} {% band header="Avoiding the flash of unstyled content (FOUC)" %} @@ -148,12 +148,12 @@ Altogether your import map code could look something like this [Lit Playground D --reveal-duration: 0.2s; } - pf-v5-card { + pf-v6-card { opacity: 1; transition: opacity var(--reveal-duration) ease var(--reveal-delay); } - pf-v5-card:not(:defined) { + pf-v6-card:not(:defined) { opacity: 0; } @@ -161,18 +161,18 @@ Altogether your import map code could look something like this [Lit Playground D - + - +

No FOUC

Content will remain hidden until component definitions are loaded.

-
+ ``` diff --git a/docs/home.css b/docs/home.css index f936eb299c..f02e7ffef0 100644 --- a/docs/home.css +++ b/docs/home.css @@ -80,7 +80,7 @@ #lightw > a { grid-area: link; } #lightw > h2 { grid-area: h; } #lightw > p:not(.subtitle) { grid-area: p; } -#lightw > pf-v5-card { grid-area: card; } +#lightw > pf-v6-card { grid-area: card; } #lightw > pf-v5-progress-stepper { grid-area: step; } #lightw > pre { grid-area: snip; margin-block: 0; } diff --git a/docs/index.html b/docs/index.html index 6f5eacfc09..15542c1c5c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -31,14 +31,14 @@

Lightweight

{% renderFile "./docs/_snippets/card-html.md" %} - +

Card component

PatternFly Elements are custom HTML elements that work everywhere. The Card element has header and footer slots for things like titles and actions.

- More about pf-v5-card -
+ More about pf-v6-card + View the rest of the components diff --git a/docs/main.css b/docs/main.css index 8e566b44d6..c9e6e07aea 100644 --- a/docs/main.css +++ b/docs/main.css @@ -799,7 +799,7 @@ code { min-width: 0; } -pf-v5-card > h3 { +pf-v6-card > h3 { margin-top: 0; } diff --git a/docs/main.mjs b/docs/main.mjs index d56d42bf2d..2e715da67b 100644 --- a/docs/main.mjs +++ b/docs/main.mjs @@ -4,7 +4,7 @@ import '@patternfly/elements/pf-v5-accordion/pf-v5-accordion.js'; import '@patternfly/elements/pf-v5-alert/pf-v5-alert.js'; import '@patternfly/elements/pf-v5-back-to-top/pf-v5-back-to-top.js'; import '@patternfly/elements/pf-v5-button/pf-v5-button.js'; -import '@patternfly/elements/pf-v5-card/pf-v5-card.js'; +import '@patternfly/elements/pf-v6-card/pf-v6-card.js'; import '@patternfly/elements/pf-v5-chip/pf-v5-chip.js'; import '@patternfly/elements/pf-v5-clipboard-copy/pf-v5-clipboard-copy.js'; import '@patternfly/elements/pf-v5-code-block/pf-v5-code-block.js'; diff --git a/docs/quick-start.md b/docs/quick-start.md index 8a6eb53376..a73a623071 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -58,21 +58,21 @@ production.

Card components

- +

Card 1

Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis laboriosam eum saepe eius tempora sequi eligendi repudiandae aspernatur beatae totam voluptatum facere unde, vitae inventore eveniet accusamus nulla recusandae aliquam.

More about PatternFly Elements -
- + +

Card 2

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eaque necessitatibus sapiente aliquam recusandae maxime consectetur magnam ipsa veniam expedita molestiae. Quis officia minima libero repellat laboriosam sit nemo porro laborum.

Get started -
- + +

Card 3

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Voluptate iusto laboriosam molestias, quidem ab voluptates nihil earum sed! Esse repellat quo ut numquam mollitia quis saepe aspernatur fuga error in!

About web components -
+ View all of the components
diff --git a/elements/pf-v5-button/demo/user-role.html b/elements/pf-v5-button/demo/user-role.html index bc0d40bc20..b539de8df1 100644 --- a/elements/pf-v5-button/demo/user-role.html +++ b/elements/pf-v5-button/demo/user-role.html @@ -1,4 +1,4 @@ - +

Button Role Example

Button Role Example However, we recommend always using <pf-v5-tabs>, and avoiding custom implementations of existing design system patterns - + diff --git a/elements/pf-v5-card/demo/modifiers.html b/elements/pf-v5-card/demo/modifiers.html deleted file mode 100644 index 85f9a21124..0000000000 --- a/elements/pf-v5-card/demo/modifiers.html +++ /dev/null @@ -1,107 +0,0 @@ -
-
- - - - - - - - - - - - - - - - - -
- -
- -

Lightest card

-

This is the lightest pf-v5-card and a link, and a visited link with border.

- Try - Buy -
-
-
- - - - diff --git a/elements/pf-v5-card/demo/ssr.html b/elements/pf-v5-card/demo/ssr.html deleted file mode 100644 index 7a21fac5a2..0000000000 --- a/elements/pf-v5-card/demo/ssr.html +++ /dev/null @@ -1,50 +0,0 @@ - -

Header

-

Body

- Footer -
- - -

Body

-
- - -

Header

-
- - -

Header

-

Body

-
- - -

Header

- Footer -
- - -

Body

- Footer -
- - - Footer - - - diff --git a/elements/pf-v5-card/demo/title-inline-with-images-and-actions.html b/elements/pf-v5-card/demo/title-inline-with-images-and-actions.html deleted file mode 100644 index d3a7a0f355..0000000000 --- a/elements/pf-v5-card/demo/title-inline-with-images-and-actions.html +++ /dev/null @@ -1,22 +0,0 @@ - -

This is a really really really really really really really really really really long header

- - - - Action - Link - Disabled Action - Disabled Link -
- Separated Action - Separated Link -
-
- Body - Footer -
- - diff --git a/elements/pf-v5-card/docs/CHANGELOG.old.md b/elements/pf-v5-card/docs/CHANGELOG.old.md deleted file mode 100644 index e9ed746827..0000000000 --- a/elements/pf-v5-card/docs/CHANGELOG.old.md +++ /dev/null @@ -1,101 +0,0 @@ -# @patternfly/pfe-card - -## 2.0.0-next.7 - -### Major Changes - -- 02989530: ## Patternfly Card 1:1 🎉 - - This release migrates Patternfly Card `pfe-card` to Patternfly 1:1 and matches the style and functionality to PFv4. - - This will have several breaking changes from the existing `pfe-card`. Many of the attributes will be moving to `rh-card`. - - ### NEW: Features and Updates - - - Styles now match PFv4 Card. - - Size Attribute: `small` is updated to `compact` and `large` is now an option - - Example: ``. - - Rounded Attribute: NEW! Optionally applies a border radius for the drop shadow and/or border of the card. - - Example: `` - - FullHeight Attribute: NEW! Optionally allow the card to take up the full height of the parent element. - - Example: `` - - Plain Attribute: NEW! Removes the border on the card element. - - Example: `` - - ### Breaking Changes - - - No longer includes an `imgSrc` attribute. - - No longer has a dark mode theme. - - No longer includes a `border` attribute. - -## 2.0.0-next.6 - -### Patch Changes - -- 07ad1d3d: Updates use of `` -- Updated dependencies [07ad1d3d] - - @patternfly/pfe-core@2.0.0-next.10 - -## 2.0.0-next.5 - -### Patch Changes - -- bfad8b4b: Updates dependencies -- Updated dependencies [bfad8b4b] - - @patternfly/pfe-core@2.0.0-next.8 - -## 2.0.0-next.4 - -### Patch Changes - -- 9caed6ad: [Issue #1984] Adding padding to top of image if header is present in card and image is set to overflow top. - -## 2.0.0-next.3 - -### Major Changes - -- 6a2a0407: [View commit message here](https://gist.github.com/heyMP/200fc0b840690541475923facba393ab) - -### Patch Changes - -- Updated dependencies [6a2a0407] - - @patternfly/pfe-core@2.0.0-next.4 - -## 2.0.0-next.2 - -### Patch Changes - -- 70bfd93f: build pfe-card and pfe-cta's light dom CSS during the `build` command - -## 2.0.0-next.1 - -### Patch Changes - -- 447b2d75: Remove `esbuild` export condition, as this anyways was a runtime error -- Updated dependencies [447b2d75] - - @patternfly/pfe-core@2.0.0-next.3 - -## 2.0.0-next.0 - -### Major Changes - -- cc006c44: ## 🔥 Migrate to Lit - - This release migrates `` to LitElement. - - ### NEW: CSS Shadow Parts! - - - Adds `header`, `body`, and `footer` CSS parts to `` - - ### Breaking Changes - - - Initial render is now [asynchronous](https://lit.dev/docs/components/lifecycle/#reactive-update-cycle) - If your code assumes that shadow DOM is ready once the element is constructed, update it to `await element.updateComplete` - - Deprecates `pfe-card--header` and `pfe-card--footer` slots. Use `header` and `footer` instead - - See [docs](https://patternflyelements.org/components/card/) for more info - -### Patch Changes - -- Updated dependencies [e8788c72] - - @patternfly/pfe-core@2.0.0-next.0 diff --git a/elements/pf-v5-card/docs/pf-v5-card.md b/elements/pf-v5-card/docs/pf-v5-card.md deleted file mode 100644 index bd05d21ba5..0000000000 --- a/elements/pf-v5-card/docs/pf-v5-card.md +++ /dev/null @@ -1,147 +0,0 @@ -{% renderInstallation %} {% endrenderInstallation %} - -{% renderOverview %} - - Cards are flexible surfaces used to group information in a small layout. They give small previews of information or provide secondary content in relation to the content it's near. Several cards can be used together to group related information. - - -

Header

-

This is the default card

- Link in the footer -
-{% endrenderOverview %} - -{% band header="Usage" %} - - ### Basic cards - - ### Modifiers - {% htmlexample %} - -

Header

-

This is the compact card

- Link in the footer -
- {% endhtmlexample %} - - ### Large card - {% htmlexample %} - -

Large card

-

This is the large card

- Link in the footer -
- {% endhtmlexample %} - - ### Rounded card - {% htmlexample %} - -

Header

-

This is the rounded card

- Link in the footer -
- {% endhtmlexample %} - - ### Full Height card - {% htmlexample %} - -

Header

-

This is the full height card

- Link in the footer -
- {% endhtmlexample %} - - ### Plain card - {% htmlexample %} - -

Header

-

This is the plain card

- Link in the footer -
- {% endhtmlexample %} - - ### Header images and actions - You can include header images and actions in the `header` slot, along with a - title in the `title` slot. The following example includes an SVG image, and - also includes a kebab dropdown. - - {% htmlexample %} - - - - - - - - - Action - Link - Disabled Action - Disabled Link -
- Separated Action - Separated Link -
-
-

Title

- Body - Footer -
- {% endhtmlexample %} - - ### Title inline with images and actions - Slotting the `

` into the `header` slot, instead of the `title` slot will - style it inline with any images or actions. - - {% htmlexample %} - -

This is a really really really really really really really really really really long header

- - - - Action - Link - Disabled Action - Disabled Link -
- Separated Action - Separated Link -
-
- Body - Footer -
- {% endhtmlexample %} - -{% endband %} - -{% renderSlots %} - All slots are optional. If the slot is not defined, the content will be added to the `body` region of the card. -{% endrenderSlots %} - -{% renderAttributes %} - - #### `overflow` (child element attribute) - Optionally allows an image or element to overflow the padding on the container. This property should be added to the direct child of the slot such as on an image tag; should be added to the element that you want to overflow the container. Accepts: `top`, `right`, `bottom`, `left`. - -{% endrenderAttributes %} - -{% renderProperties %}{% endrenderProperties %} - -{% renderMethods %}{% endrenderMethods %} - -{% renderEvents %}{% endrenderEvents %} - -{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} - -{% renderCssParts %}{% endrenderCssParts %} - - diff --git a/elements/pf-v5-card/docs/screenshot.png b/elements/pf-v5-card/docs/screenshot.png deleted file mode 100644 index e7d50a6e7b..0000000000 Binary files a/elements/pf-v5-card/docs/screenshot.png and /dev/null differ diff --git a/elements/pf-v5-card/pf-v5-card.css b/elements/pf-v5-card/pf-v5-card.css deleted file mode 100644 index 6632b3cb9b..0000000000 --- a/elements/pf-v5-card/pf-v5-card.css +++ /dev/null @@ -1,226 +0,0 @@ -:host { - /** BackgroundColor for the card */ - --pf-v5-c-card--BackgroundColor: var(--pf-global--BackgroundColor--100, #fff); - /** BoxShadow for the card */ - --pf-v5-c-card--BoxShadow: var(--pf-global--BoxShadow--sm, 0 0.0625rem 0.125rem 0 rgba(3, 3, 3, 0.12), 0 0 0.125rem 0 rgba(3, 3, 3, 0.06)); - --pf-v5-c-card--first-child--PaddingTop: var(--pf-global--spacer--lg, 1.5rem); - --pf-v5-c-card--child--PaddingRight: var(--pf-global--spacer--lg, 1.5rem); - --pf-v5-c-card--child--PaddingBottom: var(--pf-global--spacer--lg, 1.5rem); - --pf-v5-c-card--child--PaddingLeft: var(--pf-global--spacer--lg, 1.5rem); - --pf-v5-c-card--c-divider--child--PaddingTop: var(--pf-global--spacer--lg, 1.5rem); - /** FontFamily for the card title */ - --pf-v5-c-card__title--FontFamily: var(--pf-global--FontFamily--heading--sans-serif, "RedHatDisplay", "Overpass", overpass, helvetica, arial, sans-serif); - /** FontSize for the card title */ - --pf-v5-c-card__title--FontSize: var(--pf-global--FontSize--md, 1rem); - /** FontWeight for the card title */ - --pf-v5-c-card__title--FontWeight: var(--pf-global--FontWeight--bold, 700); - --pf-v5-c-card__title--not--last-child--PaddingBottom: var(--pf-global--spacer--md, 1rem); - --pf-v5-c-card__body--FontSize: var(--pf-global--FontSize--md, 1rem); - --pf-v5-c-card__footer--FontSize: var(--pf-global--FontSize--md, 1rem); - --pf-v5-c-card__actions--PaddingLeft: var(--pf-global--spacer--md, 1rem); - --pf-v5-c-card__actions--child--MarginLeft: var(--pf-global--spacer--sm, 0.5rem); - --pf-v5-c-card__header-toggle--MarginTop: calc(var(--pf-global--spacer--form-element, 0.375rem) * -1); - --pf-v5-c-card__header-toggle--MarginRight: var(--pf-global--spacer--xs, 0.25rem); - --pf-v5-c-card__header-toggle--MarginBottom: calc(var(--pf-global--spacer--form-element, 0.375rem) * -1); - --pf-v5-c-card__header-toggle--MarginLeft: calc(var(--pf-global--spacer--md, 1rem) * -1); - --pf-v5-c-card__header-toggle-icon--Transition: var(--pf-global--Transition, all 250ms cubic-bezier(0.42, 0, 0.58, 1)); - --pf-v5-c-card--m-expanded__header-toggle-icon--Rotate: 90deg; - --pf-v5-c-card--m-hoverable--hover--BoxShadow: var(--pf-global--BoxShadow--lg, 0 0.5rem 1rem 0 rgba(3, 3, 3, 0.16), 0 0 0.375rem 0 rgba(3, 3, 3, 0.08)); - --pf-v5-c-card--m-selectable--hover--BoxShadow: var(--pf-global--BoxShadow--lg, 0 0.5rem 1rem 0 rgba(3, 3, 3, 0.16), 0 0 0.375rem 0 rgba(3, 3, 3, 0.08)); - --pf-v5-c-card--m-selectable--focus--BoxShadow: var(--pf-global--BoxShadow--lg, 0 0.5rem 1rem 0 rgba(3, 3, 3, 0.16), 0 0 0.375rem 0 rgba(3, 3, 3, 0.08)); - --pf-v5-c-card--m-selectable--active--BoxShadow: var(--pf-global--BoxShadow--lg, 0 0.5rem 1rem 0 rgba(3, 3, 3, 0.16), 0 0 0.375rem 0 rgba(3, 3, 3, 0.08)); - --pf-v5-c-card--m-selectable--m-selected--BoxShadow: var(--pf-global--BoxShadow--lg, 0 0.5rem 1rem 0 rgba(3, 3, 3, 0.16), 0 0 0.375rem 0 rgba(3, 3, 3, 0.08)); - --pf-v5-c-card--m-selectable--m-selected--before--Height: var(--pf-global--BorderWidth--lg, 3px); - --pf-v5-c-card--m-selectable--m-selected--before--BackgroundColor: var(--pf-global--active-color--100, #06c); - --pf-v5-c-card--m-hoverable-raised--hover--BoxShadow: var(--pf-global--BoxShadow--md, 0 0.25rem 0.5rem 0rem rgba(3, 3, 3, 0.12), 0 0 0.25rem 0 rgba(3, 3, 3, 0.06)); - --pf-v5-c-card--m-hoverable-raised--hover--before--BackgroundColor: var(--pf-global--active-color--400, #73bcf7); - --pf-v5-c-card--m-selectable-raised--before--Right: 0; - --pf-v5-c-card--m-selectable-raised--before--Bottom: 0; - --pf-v5-c-card--m-selectable-raised--before--Left: 0; - --pf-v5-c-card--m-flat--m-selectable-raised--before--Right: calc(-1 * var(--pf-v5-c-card--m-flat--BorderWidth)); - --pf-v5-c-card--m-flat--m-selectable-raised--before--Bottom: calc(-1 * var(--pf-v5-c-card--m-flat--BorderWidth)); - --pf-v5-c-card--m-flat--m-selectable-raised--before--Left: calc(-1 * var(--pf-v5-c-card--m-flat--BorderWidth)); - --pf-v5-c-card--m-selectable-raised--before--Height: var(--pf-global--BorderWidth--xl, 4px); - --pf-v5-c-card--m-selectable-raised--before--BackgroundColor: transparent; - --pf-v5-c-card--m-selectable-raised--before--Transition: none; - --pf-v5-c-card--m-selectable-raised--before--ScaleY: 1; - --pf-v5-c-card--m-selectable-raised--before--TranslateY: 0; - --pf-v5-c-card--m-selectable-raised--hover--BoxShadow: var(--pf-global--BoxShadow--md, 0 0.25rem 0.5rem 0rem rgba(3, 3, 3, 0.12), 0 0 0.25rem 0 rgba(3, 3, 3, 0.06)); - --pf-v5-c-card--m-selectable-raised--hover--before--BackgroundColor: var(--pf-global--active-color--400, #73bcf7); - --pf-v5-c-card--m-selectable-raised--focus--BoxShadow: var(--pf-global--BoxShadow--md, 0 0.25rem 0.5rem 0rem rgba(3, 3, 3, 0.12), 0 0 0.25rem 0 rgba(3, 3, 3, 0.06)); - --pf-v5-c-card--m-selectable-raised--focus--before--BackgroundColor: var(--pf-global--active-color--400, #73bcf7); - --pf-v5-c-card--m-selectable-raised--active--BoxShadow: var(--pf-global--BoxShadow--md, 0 0.25rem 0.5rem 0rem rgba(3, 3, 3, 0.12), 0 0 0.25rem 0 rgba(3, 3, 3, 0.06)); - --pf-v5-c-card--m-selectable-raised--active--before--BackgroundColor: var(--pf-global--active-color--400, #73bcf7); - --pf-v5-c-card--m-selectable-raised--m-selected-raised--before--BackgroundColor: var(--pf-global--active-color--100, #06c); - --pf-v5-c-card--m-selectable-raised--m-selected-raised--BoxShadow: var(--pf-global--BoxShadow--lg, 0 0.5rem 1rem 0 rgba(3, 3, 3, 0.16), 0 0 0.375rem 0 rgba(3, 3, 3, 0.08)); - --pf-v5-c-card--m-selectable-raised--m-selected-raised--TranslateY--base: -0.5rem; - --pf-v5-c-card--m-selectable-raised--m-selected-raised--TranslateY: var(--pf-v5-c-card--m-selectable-raised--m-selected-raised--TranslateY--base); - --pf-v5-c-card--m-flat--m-selectable-raised--m-selected-raised--TranslateY: calc(var(--pf-v5-c-card--m-selectable-raised--m-selected-raised--TranslateY--base) + var(--pf-v5-c-card--m-flat--BorderWidth)); - --pf-v5-c-card--m-rounded--m-selectable-raised--m-selected-raised--TranslateY: calc(var(--pf-v5-c-card--m-selectable-raised--m-selected-raised--TranslateY--base) + var(--pf-v5-c-card--m-rounded--BorderRadius)); - --pf-v5-c-card--m-selectable-raised--m-selected-raised--ZIndex: var(--pf-global--ZIndex--xs, 100); - --pf-v5-c-card--m-selectable-raised--m-selected-raised--Transition: transform .25s linear, box-shadow .25s linear; - --pf-v5-c-card--m-selectable-raised--m-selected-raised--before--Transition: transform .25s linear; - --pf-v5-c-card--m-selectable-raised--m-selected-raised--before--TranslateY: calc(var(--pf-v5-c-card--m-selectable-raised--m-selected-raised--TranslateY) * -1); - --pf-v5-c-card--m-selectable-raised--m-selected-raised--before--ScaleY: 2; - --pf-v5-c-card--m-non-selectable-raised--BackgroundColor: var(--pf-global--BackgroundColor--light-200, #fafafa); - --pf-v5-c-card--m-non-selectable-raised--before--BackgroundColor: var(--pf-global--disabled-color--200, #d2d2d2); - --pf-v5-c-card--m-non-selectable-raised--before--ScaleY: 2; - --pf-v5-c-card--m-flat--m-non-selectable-raised--before--BorderColor: var(--pf-global--disabled-color--200, #d2d2d2); - /** FontSize for the card body in compact size */ - --pf-v5-c-card--m-compact__body--FontSize: var(--pf-global--FontSize--sm, 0.875rem); - /** FontSize for the card footer in compact size */ - --pf-v5-c-card--m-compact__footer--FontSize: var(--pf-global--FontSize--sm, 0.875rem); - /** PaddingTop for the first child in compact size */ - --pf-v5-c-card--m-compact--first-child--PaddingTop: var(--pf-global--spacer--md, 1rem); - /** PaddingRight for children in compact size */ - --pf-v5-c-card--m-compact--child--PaddingRight: var(--pf-global--spacer--md, 1rem); - /** PaddingBottom for children in compact size */ - --pf-v5-c-card--m-compact--child--PaddingBottom: var(--pf-global--spacer--md, 1rem); - /** PaddingLeft for children in compact size */ - --pf-v5-c-card--m-compact--child--PaddingLeft: var(--pf-global--spacer--md, 1rem); - --pf-v5-c-card--m-compact--c-divider--child--PaddingTop: var(--pf-global--spacer--md, 1rem); - /** PaddingBottom for the title when not last child in compact size */ - --pf-v5-c-card--m-compact__title--not--last-child--PaddingBottom: var(--pf-global--spacer--sm, 0.5rem); - /** FontSize for the title in large size */ - --pf-v5-c-card--m-display-lg__title--FontSize: var(--pf-global--FontSize--xl, 1.25rem); - /** PaddingTop for the first child in large size */ - --pf-v5-c-card--m-display-lg--first-child--PaddingTop: var(--pf-global--spacer--xl, 2rem); - /** PaddingRight for children in large size */ - --pf-v5-c-card--m-display-lg--child--PaddingRight: var(--pf-global--spacer--xl, 2rem); - /** PaddingBottom for children in large size */ - --pf-v5-c-card--m-display-lg--child--PaddingBottom: var(--pf-global--spacer--xl, 2rem); - /** PaddingLeft for children in large size */ - --pf-v5-c-card--m-display-lg--child--PaddingLeft: var(--pf-global--spacer--xl, 2rem); - --pf-v5-c-card--m-display-lg--c-divider--child--PaddingTop: var(--pf-global--spacer--xl, 2rem); - /** PaddingBottom for the title when not last child in large size */ - --pf-v5-c-card--m-display-lg__title--not--last-child--PaddingBottom: var(--pf-global--spacer--lg, 1.5rem); - /** BorderWidth for the flat variant */ - --pf-v5-c-card--m-flat--BorderWidth: var(--pf-global--BorderWidth--sm, 1px); - /** BorderColor for the flat variant */ - --pf-v5-c-card--m-flat--BorderColor: var(--pf-global--BorderColor--100, #d2d2d2); - /** BorderRadius for the rounded variant */ - --pf-v5-c-card--m-rounded--BorderRadius: var(--pf-global--BorderRadius--sm, 3px); - /** Height for the full height variant */ - --pf-v5-c-card--m-full-height--Height: 100%; - /** BoxShadow for the plain variant */ - --pf-v5-c-card--m-plain--BoxShadow: none; - /** BackgroundColor for the plain variant */ - --pf-v5-c-card--m-plain--BackgroundColor: transparent; - --pf-v5-c-card__header--m-toggle-right--toggle--MarginRight: calc(var(--pf-global--spacer--form-element, 0.375rem) * -1); - --pf-v5-c-card__header--m-toggle-right--toggle--MarginLeft: var(--pf-global--spacer--xs, 0.25rem); - --pf-v5-c-card__header--m-toggle-right--actions--MarginRight: 0; - --pf-v5-c-card__input--focus--BorderWidth: var(--pf-global--BorderWidth--md, 2px); - --pf-v5-c-card__input--focus--BorderColor: var(--pf-global--primary-color--100, #06c); - display: flex; - flex-direction: column; - background-color: var(--pf-v5-c-card--BackgroundColor); - box-shadow: var(--pf-v5-c-card--BoxShadow); -} - -[hidden], -.empty { - display: none !important; -} - -header { - padding-block-start: var(--pf-v5-c-card--first-child--PaddingTop); - padding-block-end: var(--pf-v5-c-card__title--not--last-child--PaddingBottom); - display: flex; - flex-flow: row wrap; - align-items: center; -} - -header ::slotted(*) { - margin-block: 0 !important; - - font-family: var(--pf-v5-c-card__title--FontFamily) !important; - font-size: var(--pf-v5-c-card__title--FontSize) !important; - font-weight: var(--pf-v5-c-card__title--FontWeight) !important; -} - -header ::slotted(pf-v5-dropdown) { - margin-inline-start: auto; -} - -article { - position: relative; - height: 100%; - display: flex; - flex-direction: column; -} - -[part="header"], -[part="body"], -[part="footer"] { - padding-inline-start: var(--pf-v5-c-card--child--PaddingLeft); - padding-inline-end: var(--pf-v5-c-card--child--PaddingRight); - padding-block-end: var(--pf-v5-c-card--child--PaddingBottom); -} - -#title { - display: block; - flex: 1 0 100%; - padding-block-start: var(--pf-v5-c-card__title--not--last-child--PaddingBottom); -} - -[part="body"] { - font-size: var(--pf-v5-c-card__body--FontSize); - flex: var(--pf-v5-c-card__body--FullHeight--Flex); -} - -[part="body"] ::slotted(:not([slot]):first-of-type) { - margin-block-start: 0 !important; -} - -[part="body"] ::slotted(:not([slot]):last-of-type) { - margin-block-end: 0 !important; -} - -[part="footer"] { - margin-block-start: auto; - display: flex; - gap: 0.5em; - inset-block-end: 0; - font-size: var(--pf-v5-c-card__footer--FontSize); -} - -:host([size="compact"]) { - --pf-v5-c-card__body--FontSize: var(--pf-v5-c-card--m-compact__body--FontSize); - --pf-v5-c-card__footer--FontSize: var(--pf-v5-c-card--m-compact__footer--FontSize); - --pf-v5-c-card--first-child--PaddingTop: var(--pf-v5-c-card--m-compact--first-child--PaddingTop); - --pf-v5-c-card--child--PaddingRight: var(--pf-v5-c-card--m-compact--child--PaddingRight); - --pf-v5-c-card--child--PaddingBottom: var(--pf-v5-c-card--m-compact--child--PaddingBottom); - --pf-v5-c-card--child--PaddingLeft: var(--pf-v5-c-card--m-compact--child--PaddingLeft); - --pf-v5-c-card--c-divider--child--PaddingTop: var(--pf-v5-c-card--m-compact--c-divider--child--PaddingTop); - --pf-v5-c-card__title--not--last-child--PaddingBottom: var(--pf-v5-c-card--m-compact__title--not--last-child--PaddingBottom); -} - -:host([size="large"]) { - --pf-v5-c-card__title--FontSize: var(--pf-v5-c-card--m-display-lg__title--FontSize); - --pf-v5-c-card--first-child--PaddingTop: var(--pf-v5-c-card--m-display-lg--first-child--PaddingTop); - --pf-v5-c-card--child--PaddingRight: var(--pf-v5-c-card--m-display-lg--child--PaddingRight); - --pf-v5-c-card--child--PaddingBottom: var(--pf-v5-c-card--m-display-lg--child--PaddingBottom); - --pf-v5-c-card--child--PaddingLeft: var(--pf-v5-c-card--m-display-lg--child--PaddingLeft); - --pf-v5-c-card--c-divider--child--PaddingTop: var(--pf-v5-c-card--m-display-lg--c-divider--child--PaddingTop); - --pf-v5-c-card__title--not--last-child--PaddingBottom: var(--pf-v5-c-card--m-display-lg__title--not--last-child--PaddingBottom); -} - -:host([flat]) { - --pf-v5-c-card--BoxShadow: none; - border: var(--pf-v5-c-card--m-flat--BorderWidth) solid var(--pf-v5-c-card--m-flat--BorderColor); -} - -:host([plain]) { - --pf-v5-c-card--BoxShadow: var(--pf-v5-c-card--m-plain--BoxShadow); - --pf-v5-c-card--BackgroundColor: var(--pf-v5-c-card--m-plain--BackgroundColor); -} - -:host([rounded]) { - border-radius: var(--pf-v5-c-card--m-rounded--BorderRadius); -} - -:host([full-height]) { - height: var(--pf-v5-c-card--m-full-height--Height); - --pf-v5-c-card__body--FullHeight--Flex: 1 1 auto; -} - diff --git a/elements/pf-v5-card/pf-v5-card.ts b/elements/pf-v5-card/pf-v5-card.ts deleted file mode 100644 index 40681f2898..0000000000 --- a/elements/pf-v5-card/pf-v5-card.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { LitElement, html, type TemplateResult } from 'lit'; -import { customElement } from 'lit/decorators/custom-element.js'; -import { property } from 'lit/decorators/property.js'; -import { classMap } from 'lit/directives/class-map.js'; - -import { SlotController } from '@patternfly/pfe-core/controllers/slot-controller.js'; - -import style from './pf-v5-card.css'; - -/** - * A **card** is a square or rectangular container that can contain any kind of content. - * Cards symbolize units of information, and each one acts as an entry point for - * users to access more details. For example, in dashboards and catalog views, cards - * function as a preview of a detailed page. Cards may also be used in data displays - * like card views, or for positioning content on a page. - * @summary Gives a preview of information in a small layout - * @alias Card - */ -@customElement('pf-v5-card') -export class PfV5Card extends LitElement { - static readonly styles: CSSStyleSheet[] = [style]; - - /** - * Optionally provide a size for the card and the card contents. - * The default is set to `undefined` and provides default styles. - * Compact provides styles which decreases the padding between the sections. - * Large provides styles which increases the padding between the sections and the font size for the title, header, and footer. - */ - @property({ reflect: true }) size?: 'compact' | 'large'; - - /** - * Optionally apply a border radius for the drop shadow and/or border. - */ - @property({ type: Boolean, reflect: true }) rounded = false; - - /** - * Optionally allow the card to take up the full height of the parent element. - */ - @property({ type: Boolean, reflect: true, attribute: 'full-height' }) fullHeight = false; - - /** - * Optionally remove the border on the card container. - */ - @property({ type: Boolean, reflect: true }) plain = false; - - #slots = new SlotController(this, 'header', 'title', null, 'footer'); - - render(): TemplateResult<1> { - return html` -
- - - -
- - -
- -
- - -
-
- `; - } -} - -declare global { - interface HTMLElementTagNameMap { - 'pf-v5-card': PfV5Card; - } -} diff --git a/elements/pf-v5-card/test/pf-card.spec.ts b/elements/pf-v5-card/test/pf-card.spec.ts deleted file mode 100644 index 05293b23ed..0000000000 --- a/elements/pf-v5-card/test/pf-card.spec.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { expect, html, aTimeout, nextFrame } from '@open-wc/testing'; -import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; -import '@patternfly/pfe-tools/test/stub-logger.js'; - -import { PfV5Card } from '@patternfly/elements/pf-v5-card/pf-v5-card.js'; - -describe('', function() { - it('imperatively instantiates', function() { - expect(document.createElement('pf-v5-card')).to.be.an.instanceof(PfV5Card); - }); - - it('should upgrade', async function() { - expect(await createFixture(html``)) - .to.be.an.instanceof(customElements.get('pf-v5-card')) - .and - .to.be.an.instanceof(PfV5Card); - }); - - describe('with header and footer content', function() { - let element: PfV5Card; - let origHeight: number; - - beforeEach(async function() { - element = await createFixture(html` - -

Card 1

-

This is pf-v5-card.

- Text in footer -
`); - }); - - beforeEach(function() { - const { height } = element.getBoundingClientRect(); - origHeight = height; - }); - - describe('size', function() { - describe('unset', function() { - beforeEach(() => element.updateComplete); - beforeEach(nextFrame); - it('should have default sizing for card parts', function() { - const pStyles = getComputedStyle(element.querySelector('p')!); - const footStyles = getComputedStyle(element.querySelector('[slot=footer]')!); - expect(pStyles.getPropertyValue('font-size'), 'body font-size').to.equal('16px'); - expect(footStyles.getPropertyValue('font-size'), 'footer font-size').to.equal('16px'); - }); - }); - - describe('compact', function() { - beforeEach(async function() { - element.setAttribute('size', 'compact'); - }); - it('should be smaller', function() { - const { height } = element.getBoundingClientRect(); - expect(origHeight, 'height').to.be.greaterThan(height); - }); - }); - - describe('large', function() { - beforeEach(async function() { - element.setAttribute('size', 'large'); - }); - it('should have larger font sizes for body, footer, more padding for header', function() { - const { height } = element.getBoundingClientRect(); - expect(origHeight, 'height').to.be.lessThan(height); - const pStyles = getComputedStyle(element.querySelector('p')!); - const footStyles = getComputedStyle(element.querySelector('[slot=footer]')!); - expect(pStyles.getPropertyValue('font-size'), 'body font-size').to.equal('16px'); - expect(footStyles.getPropertyValue('font-size'), 'footer font-size').to.equal('16px'); - }); - }); - }); - - describe('rounded', function() { - describe('unset', function() { - it('should have default border radius', function() { - expect(getComputedStyle(element).getPropertyValue('border-radius')).to.equal('0px'); - }); - }); - - describe('is set', function() { - it('should have 3px border radius', async function() { - element.setAttribute('rounded', ''); - await element.updateComplete; - expect(getComputedStyle(element).getPropertyValue('border-radius')).to.equal('3px'); - }); - }); - }); - - describe('flat', function() { - describe('unset', function() { - it('should have default box shadow and border (none)', function() { - expect(getComputedStyle(element).getPropertyValue('box-shadow')).not.to.equal('none'); - expect(getComputedStyle(element).getPropertyValue('border')).to.equal('0px none rgb(0, 0, 0)'); - }); - }); - - describe('is set', function() { - it('should not have a box shadow and should have an added border', async function() { - element.setAttribute('flat', ''); - await element.updateComplete; - - expect(getComputedStyle(element).getPropertyValue('box-shadow')).to.equal('none'); - expect(getComputedStyle(element).getPropertyValue('border')).not.to.equal('none'); - }); - }); - }); - - describe('full-height', function() { - describe('is not set', function() { - it('should not be taller', async function() { - const { height } = element.getBoundingClientRect(); - expect(height).to.equal(origHeight); - }); - }); - - describe('is set', function() { - beforeEach(async function() { - element.toggleAttribute('full-height', true); - await element.updateComplete; - }); - - it('should be taller', async function() { - const { height } = element.getBoundingClientRect(); - expect(height).to.be.greaterThan(origHeight); - }); - }); - }); - - describe('plain', function() { - describe('unset', function() { - it('should have the default box shadow and background-color', function() { - expect(getComputedStyle(element).getPropertyValue('box-shadow')).not.to.be.undefined; - expect(getComputedStyle(element).getPropertyValue('background-color')).to.equal('rgb(255, 255, 255)'); - }); - }); - describe('is set', function() { - it('should have transparent background color and no box shadow', async function() { - element.setAttribute('plain', ''); - expect(getComputedStyle(element).getPropertyValue('box-shadow')).to.equal('none'); - expect(getComputedStyle(element).getPropertyValue('background-color')).to.equal('rgba(0, 0, 0, 0)'); - }); - }); - }); - - describe('accessible', function() { - it('should be accessible in both the light and shadow dom', async function() { - await expect(element).to.be.accessible(); - }); - // TODO: this has been flaky. revisit later - // it('should have an article element wrapper in the shadow dom', async function() { - // const snapshot = await a11ySnapshot(); - // const foundNode = findAccessibilityNode(snapshot, node => node.role === 'WebArea'); - // expect(foundNode, 'A node with the supplied name has been found.').to.not.be.null; - // }); - }); - }); - - it(`should render a header and footer when content for those slots are added dynamically`, async function() { - const element = await createFixture(html` - - This is the card - `); - - const header = document.createElement('h2'); - header.setAttribute('slot', 'header'); - header.textContent = 'Card Header'; - - const footer = document.createElement('div'); - footer.setAttribute('slot', 'footer'); - footer.textContent = 'This is the footer'; - - element.prepend(header); - element.appendChild(footer); - - await aTimeout(50); - - const cardHeaderSlot = - element.shadowRoot!.querySelector('slot[name="header"]')!; - const cardFooterSlot = - element.shadowRoot!.querySelector('slot[name="footer"]')!; - - await aTimeout(50); - - expect(cardHeaderSlot.assignedNodes().length).to.equal(1); - expect(cardFooterSlot.assignedNodes().length).to.equal(1); - }); -}); diff --git a/elements/pf-v6-card/README.md b/elements/pf-v6-card/README.md new file mode 100644 index 0000000000..bf2870d7ae --- /dev/null +++ b/elements/pf-v6-card/README.md @@ -0,0 +1,187 @@ +# pf-v6-card + +A square or rectangular container that holds information about a single subject. +Cards may be selectable, actionable, or expandable, and may be composed with a +header, title, subtitle, actions, one or more bodies, and a footer. + +> **TODO:** demos and examples use native ` +

Title

+ Subtitle + Body + +``` + +### Selectable card + +```html + +

First card

+ This card is selectable. +
+``` + +Use `selection="single"` and a shared `name` to create a radio group. Wrap the +group in an element with `role="radiogroup"` and an accessible label: + +```html +
+ +

First card

+ Choose one. +
+ + +

Second card

+ Choose one. +
+
+``` + +Radio groups implement roving tabindex: one card is a tab stop, and arrow keys, +Home, and End move the selection within the group. + +### Actionable card + +```html + +

View details

+ This card is a link. +
+``` + +### Expandable card + +```html + +

Title

+
+ Hidden until expanded. +
+
+``` + +### Multiple body sections + +```html + +

Title

+ Body + Secondary body +
+``` + +## Accessibility notes + +- **Accessible name.** Selectable, clickable, and expandable cards need a name + for their control. The element derives it from `accessible-label`, the title + slot, a heading in the header slot, or an image's `alt` text. Set + `accessible-label` when none of those sources provides text. +- **Single selection.** Native radios cannot group across shadow roots, so wrap + single-selection cards in `role="radiogroup"` with an accessible label. +- **Disabled cards.** `disabled` disables the card's own control only. Disable + or remove slotted interactive content from the tab order yourself. +- **Actionable-only cards.** The full-card overlay covers slotted content for + pointer users. Do not put other interactive content in an actionable-only + card; use `clickable` + `selectable` when the card needs both. + +## Divergences from React `Card` + +### Not implemented + +| React prop | Notes | +|---|---| +| `component` (on `Card`, `CardTitle`, `CardBody`, `CardFooter`) | The shadow DOM structure is fixed. To control document semantics, slot a heading element (`

`–`

`) into `title` or `header`. | +| `ouiaId`, `ouiaSafe` | No OUIA analytics attributes. | +| `innerRef` | No React ref forwarding; use `document.querySelector()` or a template ref. | +| `CardHeader.toggleButtonProps` | Arbitrary props are not spread onto the toggle. Use `accessible-label` to name the toggle. | +| `CardHeader.actions.className` | Style the elements slotted into `actions` directly. | +| `CardHeader.selectableActions.selectableActionProps` | Arbitrary props are not spread onto the control. | +| `CardHeader.selectableActions.selectableActionId` / `className` | The internal control's id and class are managed by the element. Use `accessible-label` for its name. | + +### Changed API + +| React prop | Web component | Difference | +|---|---|---| +| `isCompact` / `isLarge` | `size="compact"` / `size="large"` | Replaced by one exclusive enum. | +| `isFullHeight` | `full-height` attribute | Dropped `is` prefix | +| `isPlain` | `plain` attribute | Dropped `is` prefix | +| `isGlass` | `glass` attribute | Dropped `is` prefix | +| `isSelectable` | `selectable` attribute | Dropped `is` prefix | +| `isClickable` | `clickable` attribute | Dropped `is` prefix | +| `isSelected` | `selected` attribute | Dropped `is` prefix; reflected | +| `isClicked` | `clicked` attribute | Dropped `is` prefix; reflected | +| `isDisabled` | `disabled` attribute | Dropped `is` prefix; participates in form disabling | +| `isExpanded` | `expanded` attribute | Dropped `is` prefix; reflected | +| `CardHeader.onExpand` | `expand` / `collapse` events | Events bubble and are cancelable; the card updates `expanded` unless prevented | +| `CardHeader.selectableActions.variant` | `selection="single"` | `single` renders a radio; the default (`multiple`) renders a checkbox | +| `CardHeader.selectableActions.isHidden` | `hide-input` | Renamed; tiles use this | +| `CardHeader.selectableActions.selectableActionAriaLabel` | `accessible-label` | When omitted, the title or header text is used | +| `CardHeader.selectableActions.selectableActionAriaLabelledby` | `accessible-label` | Cross-root `aria-labelledby` is not supported; use `accessible-label` | +| `CardHeader.selectableActions.to` | `href` | Renders an anchor instead of a button | +| `CardHeader.selectableActions.isExternalLink` | `target="_blank"` | Set `target` on the card | +| `CardHeader.selectableActions.onChange` | `change` event | Bubbles; cancelable | +| `CardHeader.selectableActions.onClickAction` | `click` event | Listen for `click` on the card | +| `CardHeader.actions` | `actions` slot | Slot elements instead of an actions object | +| `CardHeader.actions.hasNoOffset` / `CardHeader.selectableActions.hasNoOffset` | `no-offset` | Moved to the card; applies to the whole actions area | +| `CardHeader.selectableActions.name` | `name` attribute / property | Moved to the card; also groups `selection="single"` radios | +| `CardHeader.isToggleRightAligned` | `toggle-right` | Renamed | +| `CardHeader.hasWrap` | `header-wrap` | Renamed | +| `CardTitle.subtitle` | `subtitle` slot | Slot text instead of a prop | +| `CardBody.isFilled` | `no-fill` | Inverted: bodies fill by default | +| `Card`, `CardHeader`, `CardTitle`, `CardBody`, `CardFooter`, `CardActions`, `CardHeaderMain`, `CardSubtitle`, `CardSelectableActions`, `CardExpandableContent` | `pf-v6-card` slots + `pf-v6-card-body` | Composition is expressed with slots; body sections are `pf-v6-card-body` elements | + +### Added + +| Web component API | Notes | +|---|---| +| `header` slot | Header images and inline titles | +| `title` / `subtitle` slots | Title row and supporting text | +| `actions` slot | Header actions | +| `expandable-content` slot | Content revealed when expanded | +| `expandable` attribute | Renders the expand/collapse toggle. React renders it when `CardHeader` receives `onExpand`; the web component owns `expanded` state. | +| `change` event | Fired before a user-driven selection change; cancelable. Not fired for programmatic changes. | +| `expand` / `collapse` events | Fired before the `expanded` state changes; cancelable | +| Form association | `pf-v6-card` is form-associated; a selected card submits `value` (default `on`) under `name` | +| `value` attribute | Form value when selected | +| `header` / `actions` / `footer` / `toggle` parts | Styling hooks for the shadow DOM containers | +| `pf-v6-card-body` element | Body section with `no-fill` support | + +The card has no default box shadow, matching the current patternfly.org card +styles. A theme can opt in with `--pf-v6-c-card--BoxShadow`. + +## Migrating from `pf-v5-card` + +| v5 API | v6 API | Notes | +|---|---|---| +| `size="compact"` / `size="large"` | Same | Preserved | +| `flat` | Removed | v6 cards have a border by default; use `plain` to remove background and border | +| `rounded` | Removed | v6 cards are rounded by default | +| `plain` | `plain` | Same | +| `full-height` | `full-height` | Same | +| `header`, `title`, default, `footer` slots | Same | Preserved | +| `part="header"`, `part="body"`, `part="footer"` | `part="header"` and `part="footer"` | Header and footer parts are preserved. The v5 `body` part wrapped a `
`; in v6 the body is slotted content, so style slotted children directly or use `pf-v6-card-body`. | +| `
` wrapper in shadow DOM | Removed | The host is the card container, avoiding landmark pollution | +| `--pf-v5-c-card--*` custom properties | `--pf-v6-c-card--*` | Renamed to the v6 token set | diff --git a/elements/pf-v6-card/demo/actionable-and-selectable.html b/elements/pf-v6-card/demo/actionable-and-selectable.html new file mode 100644 index 0000000000..68fa84b289 --- /dev/null +++ b/elements/pf-v6-card/demo/actionable-and-selectable.html @@ -0,0 +1,73 @@ +--- +name: Actionable and selectable +description: Pass both clickable and selectable to render interactive content anywhere within a selectable card. +--- + +
+ +

+ This card performs an action upon clicking the card title and is selectable. +
+ + +

Second card

+ + This card is selectable and has a link in the card body that navigates to + PatternFly. + +
+ + +

Third card

+ This card is clickable and selectable, but disabled. +
+
+ + + + diff --git a/elements/pf-v6-card/demo/actionable.html b/elements/pf-v6-card/demo/actionable.html new file mode 100644 index 0000000000..8ac09a6e27 --- /dev/null +++ b/elements/pf-v6-card/demo/actionable.html @@ -0,0 +1,37 @@ +--- +name: Actionable +description: An actionable card performs an action or navigates to a link when clicked anywhere within the card. Set href to render a link; avoid other interactive content in actionable-only cards. +--- +
+ +

First card in the actionable example

+ This card performs an action on click. +
+ + +

Second card in the actionable example

+ This card can navigate to a link on click. +
+ + +

Third card in the actionable example

+ This card is clickable but disabled. +
+
+ + + + diff --git a/elements/pf-v6-card/demo/basic-cards.html b/elements/pf-v6-card/demo/basic-cards.html new file mode 100644 index 0000000000..cfc102e258 --- /dev/null +++ b/elements/pf-v6-card/demo/basic-cards.html @@ -0,0 +1,20 @@ +--- +name: Basic cards +description: Basic cards typically have a title, body, and footer. Any of these sections may be omitted, but a body is recommended. +--- + +

Title

+ Body + Footer +
+ + + + diff --git a/elements/pf-v6-card/demo/card-header-without-title.html b/elements/pf-v6-card/demo/card-header-without-title.html new file mode 100644 index 0000000000..870d1b6c0b --- /dev/null +++ b/elements/pf-v6-card/demo/card-header-without-title.html @@ -0,0 +1,45 @@ +--- +name: Card header without title +description: Card actions and images can be placed in the card header even without a title. +--- + + + + + PatternFly + + + Body + + + + + diff --git a/elements/pf-v6-card/demo/card-with-dividers.html b/elements/pf-v6-card/demo/card-with-dividers.html new file mode 100644 index 0000000000..52c966f629 --- /dev/null +++ b/elements/pf-v6-card/demo/card-with-dividers.html @@ -0,0 +1,24 @@ +--- +name: Card with dividers +description: Dividers can be placed between sections of the card. +--- + +

Title

+
+ Body +
+ Body +
+ Footer +
+ + + + diff --git a/elements/pf-v6-card/demo/card-with-header-that-wraps.html b/elements/pf-v6-card/demo/card-with-header-that-wraps.html new file mode 100644 index 0000000000..957b3e987e --- /dev/null +++ b/elements/pf-v6-card/demo/card-with-header-that-wraps.html @@ -0,0 +1,34 @@ +--- +name: Card with header that wraps +description: Use header-wrap to allow long titles and multiple actions to wrap onto multiple rows. +--- + + +

This is a longer card title that takes up more space

+ + + + This is the card body +
+ + + + diff --git a/elements/pf-v6-card/demo/card-with-subtitle-and-actions.html b/elements/pf-v6-card/demo/card-with-subtitle-and-actions.html new file mode 100644 index 0000000000..9ca6f5139a --- /dev/null +++ b/elements/pf-v6-card/demo/card-with-subtitle-and-actions.html @@ -0,0 +1,52 @@ +--- +name: Card with subtitle and Actions +description: A card with an image, an action, and a subtitle in a single card. +--- + + + + + PatternFly + + +

Title

+ Subtitle + Body + Footer +
+ + + + diff --git a/elements/pf-v6-card/demo/card-with-subtitle.html b/elements/pf-v6-card/demo/card-with-subtitle.html new file mode 100644 index 0000000000..de1cdb6144 --- /dev/null +++ b/elements/pf-v6-card/demo/card-with-subtitle.html @@ -0,0 +1,21 @@ +--- +name: Card with subtitle +description: A basic card that also has a subtitle. +--- + +

Title

+ Subtitle + Body + Footer +
+ + + + diff --git a/elements/pf-v6-card/demo/expandable-cards.html b/elements/pf-v6-card/demo/expandable-cards.html new file mode 100644 index 0000000000..f517b22ea5 --- /dev/null +++ b/elements/pf-v6-card/demo/expandable-cards.html @@ -0,0 +1,58 @@ +--- +name: Expandable cards +description: A card can be made expandable with the expandable attribute. Content in the expandable-content slot is hidden until the caret is clicked. +--- + + + +

Title

+ +
+ Body + Footer +
+
+ + + + diff --git a/elements/pf-v6-card/demo/expandable-with-icon.html b/elements/pf-v6-card/demo/expandable-with-icon.html new file mode 100644 index 0000000000..e8ad6e29c8 --- /dev/null +++ b/elements/pf-v6-card/demo/expandable-with-icon.html @@ -0,0 +1,24 @@ +--- +name: Expandable with icon +description: An image can be placed in the card header to show an icon beside the expansion caret. +--- + + PatternFly logo +
+ Body +
+
+ + + + diff --git a/elements/pf-v6-card/demo/header-images-and-actions.html b/elements/pf-v6-card/demo/header-images-and-actions.html new file mode 100644 index 0000000000..b1dd801f4d --- /dev/null +++ b/elements/pf-v6-card/demo/header-images-and-actions.html @@ -0,0 +1,68 @@ +--- +name: Header images and actions +description: Include header actions with the actions slot. Use no-offset to remove the negative margin that helps align default-sized card titles with card actions. +--- + + + + + + PatternFly + + + +

Title

+ Body + Footer +
+ + + + diff --git a/elements/pf-v6-card/demo/index.html b/elements/pf-v6-card/demo/index.html new file mode 100644 index 0000000000..74e057f51d --- /dev/null +++ b/elements/pf-v6-card/demo/index.html @@ -0,0 +1,19 @@ +--- +name: Default +description: A basic card with a title and body. +--- + +

Title

+ Body +
+ + + + diff --git a/elements/pf-v6-card/demo/modifiers.html b/elements/pf-v6-card/demo/modifiers.html new file mode 100644 index 0000000000..2eb1aabe28 --- /dev/null +++ b/elements/pf-v6-card/demo/modifiers.html @@ -0,0 +1,57 @@ +--- +name: Modifiers +description: Enable compact, large, full-height, and plain styling with attributes. Most modifiers can be combined, except size="compact" and size="large". +--- +
+ + + + +
+ +
+ +

Title

+ Body + Footer +
+
+ + + + diff --git a/elements/pf-v6-card/demo/multi-selectable-tiles.html b/elements/pf-v6-card/demo/multi-selectable-tiles.html new file mode 100644 index 0000000000..af9ffcfe05 --- /dev/null +++ b/elements/pf-v6-card/demo/multi-selectable-tiles.html @@ -0,0 +1,60 @@ +--- +name: Multi selectable tiles +description: Selectable cards used as tiles hide their input. Do not set selection to allow more than one tile to be selected. Wrap the group in an element with role="group" and an accessible label. +--- +
+ + + + Tile header + + Tile content and description + + + + + + Tile header + + Tile content and description + + + + + + Tile header (disabled) + + Tile content and description + +
+ + + + diff --git a/elements/pf-v6-card/demo/secondary-cards.html b/elements/pf-v6-card/demo/secondary-cards.html new file mode 100644 index 0000000000..2d82b69cce --- /dev/null +++ b/elements/pf-v6-card/demo/secondary-cards.html @@ -0,0 +1,20 @@ +--- +name: Secondary cards +description: Apply secondary background color styling to a card by setting the variant attribute to secondary. +--- + +

Title

+ Body + Footer +
+ + + + diff --git a/elements/pf-v6-card/demo/selectable.html b/elements/pf-v6-card/demo/selectable.html new file mode 100644 index 0000000000..d7b089dd1a --- /dev/null +++ b/elements/pf-v6-card/demo/selectable.html @@ -0,0 +1,39 @@ +--- +name: Selectable +description: A selectable card can be selected by clicking anywhere within the card. Avoid rendering other interactive content inside a selectable-only card. +--- +
+ +

First card

+ This card is selectable. +
+ + +

Second card

+ This card is selectable. +
+ + +

Third card

+ This card is selectable but disabled. +
+
+ + + + diff --git a/elements/pf-v6-card/demo/single-selectable-tiles.html b/elements/pf-v6-card/demo/single-selectable-tiles.html new file mode 100644 index 0000000000..cba8e33fc6 --- /dev/null +++ b/elements/pf-v6-card/demo/single-selectable-tiles.html @@ -0,0 +1,60 @@ +--- +name: Single selectable tiles +description: Selectable cards used as tiles hide their input. Set selection to single, share a name, and wrap the group in an element with role="radiogroup" and an accessible label. +--- +
+ + + + Tile header + + Tile content and description + + + + + + Tile header + + Tile content and description + + + + + + Tile header (disabled) + + Tile content and description + +
+ + + + diff --git a/elements/pf-v6-card/demo/single-selectable.html b/elements/pf-v6-card/demo/single-selectable.html new file mode 100644 index 0000000000..9f44167380 --- /dev/null +++ b/elements/pf-v6-card/demo/single-selectable.html @@ -0,0 +1,39 @@ +--- +name: Single selectable +description: When a group of single selectable cards are related, pass the same name to each card and set selection to single. Wrap the group in an element with role="radiogroup" and an accessible label. +--- +
+ +

First card

+ This card is single selectable. +
+ + +

Second card

+ This card is single selectable. +
+ + +

Third card

+ This card is single selectable but disabled. +
+
+ + + + diff --git a/elements/pf-v6-card/demo/ssr.html b/elements/pf-v6-card/demo/ssr.html new file mode 100644 index 0000000000..30300e8518 --- /dev/null +++ b/elements/pf-v6-card/demo/ssr.html @@ -0,0 +1,50 @@ +--- +name: SSR +description: Server-side rendering hints for cards with slotted content. +--- +
+ +

Header

+

Body

+ Footer +
+ + +

Body

+
+ + +

Title

+
+ + +

Title

+ Subtitle +

Body

+
+ + +

Header

+ Footer +
+
+ + + + diff --git a/elements/pf-v6-card/demo/title-inline-with-images-and-actions.html b/elements/pf-v6-card/demo/title-inline-with-images-and-actions.html new file mode 100644 index 0000000000..a54befc666 --- /dev/null +++ b/elements/pf-v6-card/demo/title-inline-with-images-and-actions.html @@ -0,0 +1,38 @@ +--- +name: Title inline with images and actions +description: Place the title in the header slot to style it inline with any images or actions. +--- + + +

This is a really really really really really really really really really really long header

+ + Body + Footer +
+ + + + diff --git a/elements/pf-v6-card/demo/with-a-primary-body-section-that-fills.html b/elements/pf-v6-card/demo/with-a-primary-body-section-that-fills.html new file mode 100644 index 0000000000..fe333966c7 --- /dev/null +++ b/elements/pf-v6-card/demo/with-a-primary-body-section-that-fills.html @@ -0,0 +1,29 @@ +--- +name: With a primary body section that fills +description: Card bodies fill the available height by default. Set no-fill on all but one body to let a primary section consume the remaining space. +--- +
+ +

Title

+ Body + Second body + Primary body section that fills + Footer +
+
+ + + + diff --git a/elements/pf-v6-card/demo/with-html-heading-element.html b/elements/pf-v6-card/demo/with-html-heading-element.html new file mode 100644 index 0000000000..084bf1aee8 --- /dev/null +++ b/elements/pf-v6-card/demo/with-html-heading-element.html @@ -0,0 +1,20 @@ +--- +name: With HTML heading element +description: Use a heading element in the title slot to place the card title within the document outline. +--- + +

Title within an h4 element

+ Body + Footer +
+ + + + diff --git a/elements/pf-v6-card/demo/with-multiple-body-sections.html b/elements/pf-v6-card/demo/with-multiple-body-sections.html new file mode 100644 index 0000000000..ca529b9b05 --- /dev/null +++ b/elements/pf-v6-card/demo/with-multiple-body-sections.html @@ -0,0 +1,22 @@ +--- +name: With multiple body sections +description: Use multiple card bodies to visually separate blocks of content. +--- + +

Title

+ Body + Second body + Third body + Footer +
+ + + + diff --git a/elements/pf-v6-card/docs/pf-v6-card.md b/elements/pf-v6-card/docs/pf-v6-card.md new file mode 100644 index 0000000000..a2dca2d8c9 --- /dev/null +++ b/elements/pf-v6-card/docs/pf-v6-card.md @@ -0,0 +1,100 @@ +{% renderOverview %} + A **card** is a square or rectangular container that can contain any kind of + content. Cards symbolize units of information, and each one acts as an entry + point for users to access more details. + + +

Title

+ Body + Footer +
+{% endrenderOverview %} + +{% band header="Usage" %} + ### Basic + A card typically contains a title, body, and footer. Any of these sections may + be omitted, but a body is recommended. + + {% htmlexample %} + +

Title

+ Body + Footer +
+ {% endhtmlexample %} + + ### Header with actions + Place header images in the `header` slot, the title in the `title` slot, and + actions in the `actions` slot. + + + + {% htmlexample %} + + PatternFly + +

Title

+ Subtitle + Body +
+ {% endhtmlexample %} + + ### Selectable + A selectable card can be selected by clicking anywhere within it. Give each + control an accessible name with `accessible-label`. + + {% htmlexample %} + +

First card

+ This card is selectable. +
+ {% endhtmlexample %} + + ### Actionable + An actionable card performs an action or navigates to a link when clicked. + Set `href` to render a link. + + {% htmlexample %} + +

View details

+ This card is a link. +
+ {% endhtmlexample %} + + ### Expandable + An expandable card shows a toggle in the header. Content in the + `expandable-content` slot is hidden until the card is expanded. + + {% htmlexample %} + +

Title

+
+ Body +
+
+ {% endhtmlexample %} + + ### Multiple body sections + Use `pf-v6-card-body` for each body section. Set `no-fill` to prevent a + section from growing to fill the card. + + {% htmlexample %} + +

Title

+ Body + Secondary body +
+ {% endhtmlexample %} +{% endband %} + +{% renderSlots %}{% endrenderSlots %} + +{% renderAttributes %}{% endrenderAttributes %} + +{% renderMethods %}{% endrenderMethods %} + +{% renderEvents %}{% endrenderEvents %} + +{% renderCssParts %}{% endrenderCssParts %} + +{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} diff --git a/elements/pf-v6-card/docs/screenshot.png b/elements/pf-v6-card/docs/screenshot.png new file mode 100644 index 0000000000..497de4df83 Binary files /dev/null and b/elements/pf-v6-card/docs/screenshot.png differ diff --git a/elements/pf-v6-card/pf-v6-card-body.css b/elements/pf-v6-card/pf-v6-card-body.css new file mode 100644 index 0000000000..ba259247ca --- /dev/null +++ b/elements/pf-v6-card/pf-v6-card-body.css @@ -0,0 +1,34 @@ +:host { + display: block; + box-sizing: border-box; + padding-block-end: + var(--pf-v6-c-card--child--PaddingBlockEnd, + var(--pf-t--global--spacer--lg, 1.5rem)); + padding-inline-end: + var(--pf-v6-c-card--child--PaddingInlineEnd, + var(--pf-t--global--spacer--lg, 1.5rem)); + padding-inline-start: + var(--pf-v6-c-card--child--PaddingInlineStart, + var(--pf-t--global--spacer--lg, 1.5rem)); + font-size: + var(--pf-v6-c-card__body--FontSize, + var(--pf-t--global--font--size--body--default, 0.875rem)); + color: + var(--pf-v6-c-card__body--Color, + var(--pf-t--global--text--color--regular, #151515)); + flex: 1 1 auto; +} + +:host(:first-child) { + padding-block-start: + var(--pf-v6-c-card--first-child--PaddingBlockStart, + var(--pf-t--global--spacer--lg, 1.5rem)); +} + +:host([no-fill]) { + flex: 0 0 auto; +} + +:host([hidden]) { + display: none !important; +} diff --git a/elements/pf-v6-card/pf-v6-card-body.ts b/elements/pf-v6-card/pf-v6-card-body.ts new file mode 100644 index 0000000000..d3369c5164 --- /dev/null +++ b/elements/pf-v6-card/pf-v6-card-body.ts @@ -0,0 +1,43 @@ +import { LitElement, html, type TemplateResult } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; + +import style from './pf-v6-card-body.css'; + +/** + * A **card body** provides the main content area for a `pf-v6-card`. Use it + * when a card has more than one body section; each section provides its own + * spacing and fill behavior. A card body SHOULD contain the card's primary + * content. It fills the available height of its card by default; set `no-fill` + * to prevent a section from growing. + * + * @summary Provides a body section for a card + * + * @slot - Block elements containing the section content. Screen reader users navigate headings, links, and controls inside as usual; AVOID nested interactive content when the parent card is selectable-only or actionable-only. + * + * @cssprop {} --pf-v6-c-card--child--PaddingBlockEnd - Block-end padding; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card--child--PaddingInlineEnd - Inline-end padding; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card--child--PaddingInlineStart - Inline-start padding; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card--first-child--PaddingBlockStart - Block-start padding when first in the card; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card__body--FontSize - Body font size; defaults to `--pf-t--global--font--size--body--default` + * @cssprop {} --pf-v6-c-card__body--Color - Body text color; defaults to `--pf-t--global--text--color--regular` + */ +@customElement('pf-v6-card-body') +export class PfV6CardBody extends LitElement { + static readonly styles: CSSStyleSheet[] = [style]; + + /** + * Prevents the body section from filling the available height of the card. + */ + @property({ reflect: true, type: Boolean, attribute: 'no-fill' }) noFill = false; + + override render(): TemplateResult<1> { + return html``; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-v6-card-body': PfV6CardBody; + } +} diff --git a/elements/pf-v6-card/pf-v6-card.css b/elements/pf-v6-card/pf-v6-card.css new file mode 100644 index 0000000000..101f434c32 --- /dev/null +++ b/elements/pf-v6-card/pf-v6-card.css @@ -0,0 +1,593 @@ +:host { + --_bg: + var(--pf-v6-c-card--BackgroundColor, + var(--pf-t--global--background--color--primary--default, #ffffff)); --_border-color: + var(--pf-v6-c-card--BorderColor, + var(--pf-t--global--border--color--subtle, #e0e0e0)); + --_border-style: var(--pf-v6-c-card--BorderStyle, solid); + --_border-width: + var(--pf-v6-c-card--BorderWidth, + var(--pf-t--global--border--width--box--default, 1px)); + --_border-radius: + var(--pf-v6-c-card--BorderRadius, + var(--pf-t--global--border--radius--medium, 16px)); + --_box-shadow: + /* patternfly.org's card stylesheet does not apply a shadow; the + component token allows a theme to opt in. */ + var(--pf-v6-c-card--BoxShadow, none); + --_first-child-padding-block-start: + var(--pf-v6-c-card--first-child--PaddingBlockStart, + var(--pf-t--global--spacer--lg, 1.5rem)); + --_child-padding-block-end: + var(--pf-v6-c-card--child--PaddingBlockEnd, + var(--pf-t--global--spacer--lg, 1.5rem)); + --_child-padding-inline-end: + var(--pf-v6-c-card--child--PaddingInlineEnd, + var(--pf-t--global--spacer--lg, 1.5rem)); + --_child-padding-inline-start: + var(--pf-v6-c-card--child--PaddingInlineStart, + var(--pf-t--global--spacer--lg, 1.5rem)); + --_divider-padding-block-start: + var(--pf-v6-c-card--c-divider--child--PaddingBlockStart, + var(--pf-t--global--spacer--lg, 1.5rem)); + --_title-not-last-padding-block-end: + var(--pf-v6-c-card__title--not--last-child--PaddingBlockEnd, + var(--pf-t--global--spacer--md, 1rem)); + --_title-text-color: + var(--pf-v6-c-card__title-text--Color, + var(--pf-t--global--text--color--regular, #151515)); + --_title-text-font-family: + var(--pf-v6-c-card__title-text--FontFamily, + var(--pf-t--global--font--family--heading, + "Red Hat Display", "RedHatDisplay", Helvetica, Arial, sans-serif)); + --_title-text-font-size: + var(--pf-v6-c-card__title-text--FontSize, + var(--pf-t--global--font--size--heading--xs, 1rem)); + --_title-text-font-weight: + var(--pf-v6-c-card__title-text--FontWeight, + var(--pf-t--global--font--weight--heading--default, 500)); + --_title-text-line-height: + var(--pf-v6-c-card__title-text--LineHeight, + var(--pf-t--global--font--line-height--heading, 1.3)); + --_title-text-overflow-wrap: + var(--pf-v6-c-card__title-text--OverflowWrap, break-word); + --_body-font-size: + var(--pf-v6-c-card__body--FontSize, + var(--pf-t--global--font--size--body--default, 0.875rem)); + --_body-color: + var(--pf-v6-c-card__body--Color, + var(--pf-t--global--text--color--regular, #151515)); + --_footer-font-size: + var(--pf-v6-c-card__footer--FontSize, + var(--pf-t--global--font--size--body--default, 0.875rem)); + --_footer-color: + var(--pf-v6-c-card__footer--Color, + var(--pf-t--global--text--color--subtle, #4d4d4d)); + --_actions-padding-inline-start: + var(--pf-v6-c-card__actions--PaddingInlineStart, + var(--pf-t--global--spacer--md, 1rem)); + --_actions-gap: + var(--pf-v6-c-card__actions--Gap, + var(--pf-t--global--spacer--gap--action-to-action--default, 1rem)); + --_actions-margin-block-start: + var(--pf-v6-c-card__actions--MarginBlockStart, + calc(var(--pf-t--global--spacer--control--vertical--default, 0.5rem) * -1)); + --_actions-margin-block-end: + var(--pf-v6-c-card__actions--MarginBlockEnd, + calc(var(--pf-t--global--spacer--control--vertical--default, 0.5rem) * -1)); + --_header-wrap-row-gap: + var(--pf-v6-c-card__header--m-wrap--RowGap, + var(--pf-t--global--spacer--sm, 0.5rem)); + --_header-wrap-column-gap: + var(--pf-v6-c-card__header--m-wrap--ColumnGap, + var(--pf-t--global--spacer--md, 1rem)); + --_toggle-margin-block-start: + var(--pf-v6-c-card__header-toggle--MarginBlockStart, + calc(var(--pf-t--global--spacer--control--vertical--default, 0.5rem) * -1)); + --_toggle-margin-block-end: + var(--pf-v6-c-card__header-toggle--MarginBlockEnd, + calc(var(--pf-t--global--spacer--control--vertical--default, 0.5rem) * -1)); + --_toggle-margin-inline-end: + var(--pf-v6-c-card__header-toggle--MarginInlineEnd, + var(--pf-t--global--spacer--gap--text-to-element--default, 0.5rem)); + --_toggle-margin-inline-start: + var(--pf-v6-c-card__header-toggle--MarginInlineStart, + calc(var(--pf-t--global--spacer--action--horizontal--plain--default, 0.5rem) * -1)); + --_toggle-icon-transition-timing-function: + var(--pf-v6-c-card__header-toggle-icon--TransitionTimingFunction, + var(--pf-t--global--motion--timing-function--default, ease)); + --_toggle-icon-transition-duration: + var(--pf-v6-c-card__header-toggle-icon--TransitionDuration, + var(--pf-t--global--motion--duration--icon--default, 100ms)); + --_toggle-icon-rotate: + var(--pf-v6-c-card--m-expanded__header-toggle-icon--Rotate, -180deg); + + display: flex; + position: relative; + flex-direction: column; + overflow: auto; + box-sizing: border-box; + background-color: var(--_bg); + border-radius: var(--_border-radius); + box-shadow: var(--_box-shadow); +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +:host([hidden]) { + display: none; +} + +:host::before { + position: absolute; + inset: 0; + pointer-events: none; + content: ""; + border: + var(--_border-width) + var(--_border-style) + var(--_border-color); + border-radius: inherit; +} + +[hidden] { + display: none !important; +} + +/* Modifiers */ + +:host([size="compact"]) { + --pf-v6-c-card--first-child--PaddingBlockStart: + var(--pf-v6-c-card--m-compact--first-child--PaddingBlockStart, + var(--pf-t--global--spacer--md, 1rem)); + --pf-v6-c-card--child--PaddingBlockEnd: + var(--pf-v6-c-card--m-compact--child--PaddingBlockEnd, + var(--pf-t--global--spacer--md, 1rem)); + --pf-v6-c-card--child--PaddingInlineEnd: + var(--pf-v6-c-card--m-compact--child--PaddingInlineEnd, + var(--pf-t--global--spacer--md, 1rem)); + --pf-v6-c-card--child--PaddingInlineStart: + var(--pf-v6-c-card--m-compact--child--PaddingInlineStart, + var(--pf-t--global--spacer--md, 1rem)); + --pf-v6-c-card--c-divider--child--PaddingBlockStart: + var(--pf-v6-c-card--m-compact--c-divider--child--PaddingBlockStart, + var(--pf-t--global--spacer--md, 1rem)); + --pf-v6-c-card__title--not--last-child--PaddingBlockEnd: + var(--pf-v6-c-card--m-compact__title--not--last-child--PaddingBlockEnd, + var(--pf-t--global--spacer--sm, 0.5rem)); + --pf-v6-c-card__title-text--FontSize: + var(--pf-v6-c-card--m-compact__title-text--FontSize, + var(--pf-t--global--font--size--heading--xs, 1rem)); + --pf-v6-c-card__body--FontSize: + var(--pf-v6-c-card--m-compact__body--FontSize, + var(--pf-t--global--font--size--body--default, 0.875rem)); + --pf-v6-c-card__footer--FontSize: + var(--pf-v6-c-card--m-compact__footer--FontSize, + var(--pf-t--global--font--size--body--sm, 0.75rem)); +} + +:host([size="large"]) { + --pf-v6-c-card--first-child--PaddingBlockStart: + var(--pf-v6-c-card--m-display-lg--first-child--PaddingBlockStart, + var(--pf-t--global--spacer--xl, 2rem)); + --pf-v6-c-card--child--PaddingBlockEnd: + var(--pf-v6-c-card--m-display-lg--child--PaddingBlockEnd, + var(--pf-t--global--spacer--xl, 2rem)); + --pf-v6-c-card--child--PaddingInlineEnd: + var(--pf-v6-c-card--m-display-lg--child--PaddingInlineEnd, + var(--pf-t--global--spacer--xl, 2rem)); + --pf-v6-c-card--child--PaddingInlineStart: + var(--pf-v6-c-card--m-display-lg--child--PaddingInlineStart, + var(--pf-t--global--spacer--xl, 2rem)); + --pf-v6-c-card--c-divider--child--PaddingBlockStart: + var(--pf-v6-c-card--m-display-lg--c-divider--child--PaddingBlockStart, + var(--pf-t--global--spacer--xl, 2rem)); + --pf-v6-c-card__title--not--last-child--PaddingBlockEnd: + var(--pf-v6-c-card--m-display-lg__title--not--last-child--PaddingBlockEnd, + var(--pf-t--global--spacer--lg, 1.5rem)); + --pf-v6-c-card__title-text--FontSize: + var(--pf-v6-c-card--m-display-lg__title-text--FontSize, + var(--pf-t--global--font--size--heading--md, 1.25rem)); + --pf-v6-c-card__body--FontSize: + var(--pf-v6-c-card--m-display-lg__body--FontSize, + var(--pf-t--global--font--size--body--lg, 1rem)); + --pf-v6-c-card__footer--FontSize: + var(--pf-v6-c-card--m-display-lg__footer--FontSize, + var(--pf-t--global--font--size--body--default, 0.875rem)); +} + +:host([variant="secondary"]) { + --_bg: + var(--pf-v6-c-card--m-secondary--BackgroundColor, + var(--pf-t--global--background--color--secondary--default, #f2f2f2)); +} + +:host([plain]) { + --_bg: var(--pf-v6-c-card--m-plain--BackgroundColor, transparent); + --_border-color: var(--pf-v6-c-card--m-plain--BorderColor, transparent); + --_box-shadow: var(--pf-v6-c-card--m-plain--BoxShadow, none); +} + +:host([full-height]) { + height: var(--pf-v6-c-card--m-full-height--Height, 100%); +} + +:host([glass]) { + --_bg: + var(--pf-v6-c-card--m-glass--BackgroundColor, + var(--pf-t--global--background--color--glass--primary--default, rgba(255, 255, 255, 0.5))); + --_border-color: + var(--pf-v6-c-card--m-glass--BorderColor, + var(--pf-t--global--border--color--glass--default, rgba(0, 0, 0, 0.1))); + --_border-width: + var(--pf-v6-c-card--m-glass--BorderWidth, + var(--pf-t--global--border--width--glass--default, 1px)); + --_box-shadow: + var(--pf-v6-c-card--m-glass--BoxShadow, + var(--pf-t--global--box-shadow--glass--default, none)); + backdrop-filter: + var(--pf-v6-c-card--m-glass--BackdropFilter, + var(--pf-t--global--background--filter--glass--blur--primary, blur(16px))); +} + +/* Selectable and clickable cards */ + +:host([selectable]), +:host([clickable]) { + isolation: isolate; + --_border-color: + var(--pf-v6-c-card--m-selectable--BorderColor, + var(--pf-t--global--border--color--control--default, #d2d2d2)); + --_border-width: + var(--pf-v6-c-card--m-selectable--BorderWidth, + var(--pf-t--global--border--width--box--default, 1px)); +} + +/* Cards used as tiles */ + +:host([selectable][hide-input]:not([disabled])) { + --_bg: + var(--pf-v6-c-card--m-as-tile--BackgroundColor, + var(--pf-t--global--background--color--control--default, #ffffff)); +} + +:host([selectable][hide-input]:not([selected], [clicked], [disabled])) { + --_border-color: + var(--pf-v6-c-card--m-as-tile--BorderColor, + var(--pf-t--global--border--color--control--default, #d2d2d2)); +} + +:host([selectable]:hover:not([selected], [clicked])), +:host([clickable]:hover:not([selected], [clicked])) { + --_border-color: + var(--pf-v6-c-card--m-selectable--hover--BorderColor, + var(--pf-t--global--border--color--hover, #0066cc)); + --_border-width: + var(--pf-v6-c-card--m-selectable--hover--BorderWidth, + var(--pf-t--global--border--width--box--hover, 2px)); +} + +:host([selectable]:focus-within:not([selected], [clicked])), +:host([clickable]:focus-within:not([selected], [clicked])) { + --_border-color: + var(--pf-v6-c-card--m-selectable--focus--BorderColor, + var(--pf-t--global--border--color--hover, #0066cc)); + --_border-width: + var(--pf-v6-c-card--m-selectable--focus--BorderWidth, + var(--pf-t--global--border--width--box--hover, 2px)); +} + +:host([selectable][selected]) { + --_border-color: + var(--pf-v6-c-card--m-selectable--m-selected--BorderColor, + var(--pf-t--global--border--color--clicked, #0066cc)); + --_border-width: + var(--pf-v6-c-card--m-selectable--m-selected--BorderWidth, + var(--pf-t--global--border--width--box--clicked, 3px)); +} + +:host([clickable][clicked]) { + --_border-color: + var(--pf-v6-c-card--m-clickable--m-current--BorderColor, + var(--pf-t--global--border--color--clicked, #0066cc)); + --_border-width: + var(--pf-v6-c-card--m-clickable--m-current--BorderWidth, + var(--pf-t--global--border--width--box--clicked, 3px)); +} + +:host([selectable][selected]:focus-within) { + --_border-color: + var(--pf-v6-c-card--m-selectable--m-selected--focus--BorderColor, + var(--pf-t--global--border--color--clicked, #0066cc)); + --_border-width: + var(--pf-v6-c-card--m-selectable--m-selected--focus--BorderWidth, + var(--pf-t--global--border--width--box--clicked, 3px)); +} + +/* A selected card's border does not change on focus, so add an independent ring */ +:host([selectable]:focus-within) { + outline: + var(--pf-t--global--border--width--box--hover, 2px) + solid + var(--pf-t--global--border--color--clicked, #0066cc); + outline-offset: 2px; +} + +:host([disabled][selectable]), +:host([disabled][clickable]) { + pointer-events: none; + --_bg: + var(--pf-v6-c-card--m-selectable--m-disabled--BackgroundColor, + var(--pf-t--global--background--color--disabled--default, #f2f2f2)); + --_border-color: + var(--pf-v6-c-card--m-selectable--m-disabled--BorderColor, transparent); + --pf-v6-c-card__title-text--Color: + var(--pf-v6-c-card--m-selectable--m-disabled__title-text--Color, + var(--pf-t--global--text--color--on-disabled, #a3a3a3)); + --pf-v6-c-card__body--Color: + var(--pf-v6-c-card--m-selectable--m-disabled__body--Color, + var(--pf-t--global--text--color--on-disabled, #a3a3a3)); + --pf-v6-c-card__footer--Color: + var(--pf-v6-c-card--m-selectable--m-disabled__footer--Color, + var(--pf-t--global--text--color--on-disabled, #a3a3a3)); +} + +/* Header */ + +#header { + display: flex; + flex-direction: column; + padding-block-start: var(--_first-child-padding-block-start); + padding-block-end: var(--_title-not-last-padding-block-end); + padding-inline: + var(--_child-padding-inline-start) + var(--_child-padding-inline-end); +} + +#header[data-bare] { + padding-block: 0; +} + +#top { + display: flex; + flex-direction: row; + align-items: center; +} + +#main { + flex-grow: 1; + min-inline-size: 0; +} + +::slotted([slot="header"]) { + max-inline-size: 100%; + block-size: auto; +} + +#title { + display: flex; + flex-direction: column; +} + +#title[data-offset] { + padding-block-start: var(--_title-not-last-padding-block-end); +} + +#actions { + display: flex; + gap: var(--_actions-gap); + align-items: center; + align-self: flex-start; + margin-block-start: var(--_actions-margin-block-start); + margin-block-end: var(--_actions-margin-block-end); + margin-inline-start: auto; + padding-inline-start: var(--_actions-padding-inline-start); +} + +:host([no-offset]) #actions { + --_actions-margin-block-start: 0; + --_actions-margin-block-end: 0; +} + +:host([clickable]:not([selectable])) #actions { + --_actions-padding-inline-start: 0; + --_actions-margin-block-start: 0; + --_actions-margin-block-end: 0; +} + +:host([header-wrap]) #top { + flex-wrap: wrap; + row-gap: var(--_header-wrap-row-gap); + column-gap: var(--_header-wrap-column-gap); +} + +:host([header-wrap]) #main { + flex: 1 1 12rem; +} + +:host([header-wrap]) ::slotted([slot="actions"]) { + flex: none; +} + +:host([header-wrap]) #actions { + flex-wrap: wrap; + row-gap: var(--_header-wrap-row-gap); + margin-inline-start: 0; + --_actions-padding-inline-start: 0; +} + +/* Toggle */ + +#toggle { + display: inline-flex; + align-items: center; + justify-content: center; + align-self: flex-start; + margin-block-start: var(--_toggle-margin-block-start); + margin-block-end: var(--_toggle-margin-block-end); + margin-inline-start: var(--_toggle-margin-inline-start); + margin-inline-end: var(--_toggle-margin-inline-end); + padding: 0; + font: inherit; + color: inherit; + background: none; + border: 0; + cursor: pointer; +} + +:host([toggle-right]) #toggle { + --_toggle-margin-inline-end: + var(--pf-v6-c-card__header--m-toggle-right--toggle--MarginInlineEnd, + calc(var(--pf-t--global--spacer--action--horizontal--plain--default, 0.5rem) * -1)); + --_toggle-margin-inline-start: + var(--pf-v6-c-card__header--m-toggle-right--toggle--MarginInlineStart, + var(--pf-t--global--spacer--gap--text-to-element--default, 0.5rem)); +} + +#toggle:focus-visible { + outline: + var(--pf-t--global--border--width--box--hover, 2px) + solid + var(--pf-t--global--border--color--hover, #0066cc); + outline-offset: 2px; + border-radius: + var(--pf-t--global--border--radius--small, 4px); +} + +#toggle-icon { + display: inline-block; + transition-timing-function: var(--_toggle-icon-transition-timing-function); + transition-duration: var(--_toggle-icon-transition-duration); + transition-property: rotate; +} + +:host([expanded]) #toggle-icon { + rotate: var(--_toggle-icon-rotate); +} + +/* Selectable control */ + +#selectable-control { + flex: none; + margin: 0; + accent-color: + var(--pf-v6-c-card--m-selectable--AccentColor, + var(--pf-t--global--color--brand--default, #0066cc)); + cursor: pointer; +} + +#selectable-control:disabled { + cursor: not-allowed; +} + +:host([hide-input]) #selectable-control { + position: absolute; + inline-size: 1px; + block-size: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + white-space: nowrap; + border: 0; + clip-path: inset(50%); +} + +/* Clickable control */ + +#clickable-control { + position: absolute; + inset: 0; + background: none; + border: 0; + outline: 0; + cursor: pointer; +} + +#clickable-control:focus-visible { + outline: + var(--pf-t--global--border--width--box--hover, 2px) + solid + var(--pf-t--global--border--color--hover, #0066cc); + outline-offset: -2px; +} + +:host([disabled]) #clickable-control { + pointer-events: none; +} + +/* Body */ + +::slotted(:not([slot]):not(pf-v6-card-body):not(hr)) { + display: block; + padding-block-end: var(--_child-padding-block-end); + padding-inline: + var(--_child-padding-inline-start) + var(--_child-padding-inline-end); + font-size: var(--_body-font-size); + color: var(--_body-color); +} + +::slotted(:not([slot]):not(pf-v6-card-body):not(hr):first-child) { + padding-block-start: var(--_first-child-padding-block-start); +} + +::slotted(hr) { + inline-size: 100%; + margin: 0; + margin-block-end: var(--_divider-padding-block-start); + border: 0; + border-block-start: + var(--pf-v6-c-divider--BorderWidth, + var(--pf-t--global--border--width--divider--default, 1px)) + solid + var(--pf-v6-c-divider--BorderColor, + var(--pf-t--global--border--color--default, #d2d2d2)); +} + +/* Title and subtitle */ + +::slotted([slot="title"]), +::slotted([slot="header"]:is(h1, h2, h3, h4, h5, h6)) { + margin-block: 0; + font-family: var(--_title-text-font-family); + font-size: var(--_title-text-font-size); + font-weight: var(--_title-text-font-weight); + line-height: var(--_title-text-line-height); + color: var(--_title-text-color); + overflow-wrap: var(--_title-text-overflow-wrap); +} + +::slotted([slot="subtitle"]) { + display: block; + padding-block-start: var(--pf-t--global--spacer--sm, 0.5rem); + font-size: var(--pf-t--global--font--size--body--sm, 0.75rem); + color: var(--pf-t--global--text--color--subtle, #4d4d4d); +} + +/* Expandable content */ + +::slotted([slot="expandable-content"]) { + display: block; + --pf-v6-c-card--first-child--PaddingBlockStart: 0; +} + +/* Footer */ + +#footer { + padding-block-end: var(--_child-padding-block-end); + padding-inline: + var(--_child-padding-inline-start) + var(--_child-padding-inline-end); + font-size: var(--_footer-font-size); + color: var(--_footer-color); +} + +@media (prefers-reduced-motion: reduce) { + #toggle-icon { + transition: none; + } +} diff --git a/elements/pf-v6-card/pf-v6-card.ts b/elements/pf-v6-card/pf-v6-card.ts new file mode 100644 index 0000000000..c5c8ec29c1 --- /dev/null +++ b/elements/pf-v6-card/pf-v6-card.ts @@ -0,0 +1,657 @@ +import { LitElement, html, isServer, nothing, type PropertyValues, type TemplateResult } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; + +import { InternalsController } from '@patternfly/pfe-core/controllers/internals-controller.js'; +import { SlotController } from '@patternfly/pfe-core/controllers/slot-controller.js'; + +import './pf-v6-card-body.js'; + +import style from './pf-v6-card.css'; + +export type CardVariant = 'default' | 'secondary'; +export type CardSize = 'compact' | 'large'; +export type CardSelection = 'single' | 'multiple'; + +const INTERACTIVE_SELECTOR = [ + 'a', + 'button', + 'input', + 'label', + 'select', + 'textarea', + '[role="button"]', + '[role="link"]', + '[role="checkbox"]', + '[role="radio"]', +].join(', '); + +/** Registry of single-selection cards, keyed by their `name` attribute. */ +const cardGroups = new Map>(); + +/** + * A **card** is a container for information about a single subject. A card MAY + * combine a header, title, subtitle, actions, bodies, expandable content, and a + * footer. Selectable cards MUST have an accessible name for screen readers; + * each card control exposes its ARIA state. + * + * @summary Gives a preview of information in a small layout + * + * @slot - Card body content as block elements. Use `pf-v6-card-body` for body sections; wrap inline text in a block element so it receives body spacing. + * @slot header - Header content such as an image, a brand, or an inline heading (inline or block elements). Selectable and clickable cards need an accessible name from `accessible-label`, a heading, or image `alt` text. + * @slot title - The card title as inline text in a heading element (`

`–`

`) so it appears in the document outline. + * @slot subtitle - Short inline text displayed beneath the title. + * @slot actions - Interactive controls such as buttons, links, or inputs. Avoid other interactive content in actionable-only and selectable-only cards; screen reader users must be able to reach each control. + * @slot footer - Inline text or inline elements such as links or actions. + * @slot expandable-content - Block elements revealed when the card is expanded, typically one or more `pf-v6-card-body` elements. + * + * @fires {Event} change - Fires before a user-driven selection change. + * Carries no detail object; read `selected` from the card. + * Not fired for programmatic changes to `selected`. + * Cancelable: call `preventDefault()` to reject the change. + * @fires {Event} expand - Fires before an expandable card expands. + * Carries no detail object. + * Cancelable: call `preventDefault()` to prevent expansion. + * @fires {Event} collapse - Fires before an expandable card collapses. + * Carries no detail object. + * Cancelable: call `preventDefault()` to prevent collapse. + * + * @csspart header - The card header container + * @csspart actions - The card header actions container + * @csspart footer - The card footer container + * @csspart toggle - The expandable card toggle button + * + * @cssprop {} --pf-v6-c-card--BackgroundColor - Card background color + * @cssprop {} --pf-v6-c-card--BorderColor - Card border color + * @cssprop --pf-v6-c-card--BorderStyle - Card border style + * @cssprop {} --pf-v6-c-card--BorderWidth - Card border width + * @cssprop {} --pf-v6-c-card--BorderRadius - Card border radius + * @cssprop --pf-v6-c-card--BoxShadow - Card box shadow; defaults to none, matching patternfly.org + * @cssprop {} --pf-v6-c-card--first-child--PaddingBlockStart - Block-start padding of the first section; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card--child--PaddingBlockEnd - Block-end padding of each section; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card--child--PaddingInlineEnd - Inline-end padding of each section; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card--child--PaddingInlineStart - Inline-start padding of each section; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card--c-divider--child--PaddingBlockStart - Space after a divider between sections; defaults to `--pf-t--global--spacer--lg` + * @cssprop {} --pf-v6-c-card__title--not--last-child--PaddingBlockEnd - Block-end padding of the title; defaults to `--pf-t--global--spacer--md` + * @cssprop {} --pf-v6-c-card__title-text--Color - Title text color; defaults to `--pf-t--global--text--color--regular` + * @cssprop --pf-v6-c-card__title-text--FontFamily - Title font family; defaults to `--pf-t--global--font--family--heading` + * @cssprop {} --pf-v6-c-card__title-text--FontSize - Title font size; defaults to `--pf-t--global--font--size--heading--xs` + * @cssprop {} --pf-v6-c-card__title-text--FontWeight - Title font weight; defaults to `--pf-t--global--font--weight--heading--default` + * @cssprop {} --pf-v6-c-card__title-text--LineHeight - Title line height; defaults to `--pf-t--global--font--line-height--heading` + * @cssprop --pf-v6-c-card__title-text--OverflowWrap - Title overflow-wrap behavior + * @cssprop {} --pf-v6-c-card__body--FontSize - Body font size; defaults to `--pf-t--global--font--size--body--default` + * @cssprop {} --pf-v6-c-card__body--Color - Body text color; defaults to `--pf-t--global--text--color--regular` + * @cssprop {} --pf-v6-c-card__footer--FontSize - Footer font size; defaults to `--pf-t--global--font--size--body--default` + * @cssprop {} --pf-v6-c-card__footer--Color - Footer text color; defaults to `--pf-t--global--text--color--subtle` + * @cssprop {} --pf-v6-c-card__actions--PaddingInlineStart - Inline-start padding of the actions container; defaults to `--pf-t--global--spacer--md` + * @cssprop {} --pf-v6-c-card__actions--Gap - Gap between header actions; defaults to `--pf-t--global--spacer--gap--action-to-action--default` + * @cssprop {} --pf-v6-c-card__actions--MarginBlockStart - Block-start margin of the actions container + * @cssprop {} --pf-v6-c-card__actions--MarginBlockEnd - Block-end margin of the actions container + * @cssprop {} --pf-v6-c-card__header--m-wrap--RowGap - Row gap when the header wraps + * @cssprop {} --pf-v6-c-card__header--m-wrap--ColumnGap - Column gap when the header wraps + * @cssprop {} --pf-v6-c-card__header-toggle--MarginBlockStart - Toggle block-start margin + * @cssprop {} --pf-v6-c-card__header-toggle--MarginInlineEnd - Toggle inline-end margin + * @cssprop {} --pf-v6-c-card__header-toggle--MarginBlockEnd - Toggle block-end margin + * @cssprop {} --pf-v6-c-card__header-toggle--MarginInlineStart - Toggle inline-start margin + * @cssprop {