diff --git a/packages/react-components/components/stencil-generated/index.ts b/packages/react-components/components/stencil-generated/index.ts index ee1499a9..749d0490 100644 --- a/packages/react-components/components/stencil-generated/index.ts +++ b/packages/react-components/components/stencil-generated/index.ts @@ -14,6 +14,7 @@ export const CbpApp = /*@__PURE__*/createReactComponent('cbp-app-header'); export const CbpBadge = /*@__PURE__*/createReactComponent('cbp-badge'); export const CbpBanner = /*@__PURE__*/createReactComponent('cbp-banner'); +export const CbpBreadcrumb = /*@__PURE__*/createReactComponent('cbp-breadcrumb'); export const CbpButton = /*@__PURE__*/createReactComponent('cbp-button'); export const CbpCard = /*@__PURE__*/createReactComponent('cbp-card'); export const CbpCheckbox = /*@__PURE__*/createReactComponent('cbp-checkbox'); @@ -35,6 +36,7 @@ export const CbpHide = /*@__PURE__*/createReactComponent('cbp-icon'); export const CbpLink = /*@__PURE__*/createReactComponent('cbp-link'); export const CbpList = /*@__PURE__*/createReactComponent('cbp-list'); +export const CbpNavItem = /*@__PURE__*/createReactComponent('cbp-nav-item'); export const CbpNotice = /*@__PURE__*/createReactComponent('cbp-notice'); export const CbpPagination = /*@__PURE__*/createReactComponent('cbp-pagination'); export const CbpPanel = /*@__PURE__*/createReactComponent('cbp-panel'); @@ -46,9 +48,11 @@ export const CbpStructuredList = /*@__PURE__*/createReactComponent('cbp-structured-list-item'); export const CbpTab = /*@__PURE__*/createReactComponent('cbp-tab'); export const CbpTabPanel = /*@__PURE__*/createReactComponent('cbp-tab-panel'); +export const CbpTable = /*@__PURE__*/createReactComponent('cbp-table'); export const CbpTabs = /*@__PURE__*/createReactComponent('cbp-tabs'); export const CbpTag = /*@__PURE__*/createReactComponent('cbp-tag'); export const CbpToast = /*@__PURE__*/createReactComponent('cbp-toast'); +export const CbpTooltip = /*@__PURE__*/createReactComponent('cbp-tooltip'); export const CbpTypography = /*@__PURE__*/createReactComponent('cbp-typography'); export const CbpUniversalHeader = /*@__PURE__*/createReactComponent('cbp-universal-header'); export const CbpUsaBanner = /*@__PURE__*/createReactComponent('cbp-usa-banner'); diff --git a/packages/web-components/CHANGELOG.md b/packages/web-components/CHANGELOG.md index 90b58445..adf9f921 100644 --- a/packages/web-components/CHANGELOG.md +++ b/packages/web-components/CHANGELOG.md @@ -5,6 +5,15 @@ This CHANGELOG.md tracks the updates to the web components package of the CBP de The React components are wrappers generated from this package and will share the same changes. ## [unpublished] TBD +* First cut of the `cbp-nav-item` component for including navigation links in the Application Header. +* BREAKING: updated the `cbp-app-header` implementation. The pattern now uses the new `cbp-nav-item` component for the first "Application Name" link. Code should be updated from Storybook for the latest implementation. +* First cut of the `cbp-table` component. +* First cut of the `cbp-breadcrumb` component. +* First cut of the `cbp-tooltip` component. +* Major refactor of `cbp-dropdown`, to include: + * Cycling through options based on alphanumeric keypress by default, like a native `select`. + * Combobox functionality for filtering by search string, enabled with the `filter` property. + * Revamped the accessibility model using `aria-activedescendant` rather than sending focus to the dropdown items. Focus remains on the combobox control (button). * Published the Change log to Storybook. * Published Stencil-generated component API docs to Storybook. We will continually revisit these for completion. diff --git a/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.specs.mdx b/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.specs.mdx index c41c8730..9997a6ac 100644 --- a/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.specs.mdx +++ b/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.specs.mdx @@ -12,6 +12,7 @@ The Dropdown component offers an alternative to the native select element that c * The Dropdown component can replace the native `select` element (with caveats*) * The Dropdown component supports both single select and multi-select functionality. +* The Dropdown component supports type-ahead filtering of options (when enabled via property). * The Dropdown component will render an `input type="hidden"` element to pass the field value in a native form submission. * The Dropdown component is meant to be used in the default slot of the `cbp-form-field` component, like a native form field, providing: * Form field styles without duplication @@ -19,7 +20,7 @@ The Dropdown component offers an alternative to the native select element that c * The Dropdown options will consist of slotted child `cbp-dropdown-item` components. * For single select dropdowns, dropdown items may have individual values specified, separate from the visible label (similar to a native select option element). * For multi-select dropdowns, a `cbp-checkbox` is expected to be slotted within each dropdown item and a native checkbox control within the `cbp-checkbox`. -* The Dropdown and dropdown items must support keyboard navigation similar to a native select or menu widget. +* The Dropdown supports keyboard navigation using the combobox accessibility paradigm. * The single-select Dropdown may have default "placeholder" text specified when there is no selection. ## Technical Specifications @@ -31,20 +32,31 @@ The Dropdown component offers an alternative to the native select element that c * A disabled Dropdown does not submit a form value, as the corresponding hidden input is also disabled. * Activating the Dropdown menu may be achieved by: * Clicking on the Dropdown label or control. - * Placing focus on the Dropdown control and pressing `Enter`, `Space`, or `Down Arrow`. -* Upon activation of the Dropdown control, the dropdown menu will open and focus will automatically be moved to the selected element or the first available item (if none are selected). + * Placing focus on the Dropdown control and pressing `Enter`, `Space`, `Up Arrow`, or `Down Arrow`. +* Upon activation of the Dropdown control, the dropdown menu will open and visible focus will automatically be moved to the selected element or the first available item if none are selected. * Keyboard navigation within the dropdown menu works as follows: * `Arrow Down` and `Arrow Up` navigate the dropdown items, with wrapping at the start and end items. * `Home` jumps to the first dropdown item. * `End` jumps to the last dropdown item. * Pressing `Escape` will close the menu and return focus to the dropdown control. * Pressing `Tab` will close the menu and send focus to the next focusable element on the page. + * Clicking outside of the dropdown component will close the menu. * Pressing `Enter` will activate the focused item and mark is as selected (see below for more details) and close the menu. - -* Dropdown Items may be activated either by clicking with the mouse or pressing `Enter` on the focused item (same as a native `select` element). + * By default (without type-ahead filtering), pressing an alphanumeric character will jump to the first item starting with that character (non-case sensitive), mimicking the behavior of a native `select` element. + * Subsequent presses of the same character will cycle through items starting with that character. + * Any other keypress than the same character will interrupt this sequence and take appropriate action. + * When type-ahead filtering is enabled (via the `filter` property): + * Alphanumeric keypresses will be appended to the the search string and dropdown items filter by those that contain the search string. + * Using the arrows for keyboard navigation will move visible focus through the filtered list of options. + * The search string will be shown on the control. + * Backspace or Clear will remove a character from the search string. + * Closing the dropdown will clear the search string and filtering automatically. + +* Dropdown Items may be activated either by clicking with the mouse or pressing `Enter` or `Spacebar`. * Activating a Dropdown Item will do the following for single select dropdowns: * Emit an event, which is listened for by the parent Dropdown component. * Set the activated item as `selected` (true). + * Update the `aria-activedescendant` attribute on the control to reference the select item's `id`. * Set the `selected` property for all other Dropdown Items to `false`. * Update the visible selected value on the Dropdown control to that of the activated Dropdown Item. * Update the value of the hidden input field to the value of the clicked Dropdown Item. If no value is explicitly set, then the item's `innerText` will be used as the value. For multi-select dropdowns, this field's value is an array of checkbox values of the selected items. @@ -69,18 +81,20 @@ The Dropdown component offers an alternative to the native select element that c ### Accessibility * The associated label and `aria-describedby` are provided by the parent `cbp-form-field` component. -* The Dropdown control is actually a button with a `role="combobox"` (for single select variant). -* The Dropdown menu has a `role="listbox"`. -* The Dropdown Items have `role="option"`. -* The selected Dropdown Item has `aria-selected="true"` while the others have `aria-selected="false"`. -* The component fully supports keyboard navigation, as described above in the User Interactions section. +* The Dropdown is implemented using the combobox pattern for accessibility, which means: + * The Dropdown control is actually a button with a `role="combobox"`. + * When the dropdown is opened, focus remains on the control, while the "current" option is indicated by the `aria-activedescendant` attribute on the control, which references the visibly highlighted option's `id`. + * The Dropdown menu has a `role="listbox"`. + * The Dropdown Items have `role="option"`. + * The selected Dropdown Item has `aria-selected="true"` while the others have `aria-selected="false"`. (is this relevant since focus is not sent to the options?) + * The component fully supports keyboard navigation, as described above in the User Interactions section. ### Additional Notes and Considerations * While visually more consistent with the CBP Design System, the Dropdown component has some drawbacks compared to a native `select` element: - * A native `select` allows you to press the initial letter multiple times to cycle through those options that begin with that letter. This creates familiar interactions for common patterns such as State and Country selection where the lists are well-known. * On mobile devices, a native `select` will be presented by an enlarged overlay of radio options, which is drastically more usable in this context. * Since the main control is a button, it is disabled in "readonly" mode (while the hidden input may still submit the value). Does this cause issues in user expectations? +* For common patterns such as State and Country selection where the lists are well-known, do not enable search string filtering. Cycling through those options that begin with a letter by pressing that letter multiple times is a familiar interaction supported for many years by native select fields and search string filtering may be confusing and slower. * The `value` property should not be set explicitly if there are items marked as selected. But in the case that this occurs, the selected items will override the specified value; the value will be updated to match the selected items. * TODO: Requires additional testing with `cbp-form-field-wrapper`. @@ -110,8 +124,8 @@ The Dropdown component offers an alternative to the native select element that c * On Dropdown value property being updated externally (reactively): * Check if the new value is different from the old value. (if not, do nothing) * If so: - * select the specified dropdown item(s) based on value(s). - * Update the selectedItem reference. + * Select the specified dropdown item(s) based on value(s). + * Update the selectedItems reference. * Update the dropdown's visible label (of the selected item). * Update the focusIndex for tracking focus for keyboard navigation. * For single-select dropdowns, deselect all other dropdown items. diff --git a/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.stories.tsx b/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.stories.tsx index 256883a7..e60f16a8 100644 --- a/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.stories.tsx +++ b/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.stories.tsx @@ -49,7 +49,7 @@ export default { function generateItems(items) { const html = items.map(({ label, value=label, selected }) => { - return `${label}`; + return `${label}`; }); return html.join(''); } @@ -263,7 +263,7 @@ CountriesDropdown.args = { function generateMultiSelectItems(items, context) { const html = items.map(({ label, name, value, selected }) => { - return ` + return ` diff --git a/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.tsx b/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.tsx index b6488a16..03a4b8cb 100644 --- a/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.tsx +++ b/packages/web-components/src/components/cbp-dropdown/cbp-dropdown.tsx @@ -223,7 +223,7 @@ export class CbpDropdown { handleSlotChange(e) { - console.log('Dropdown Slot Change: ', e); + console.log('Dropdown Slot Change: ', e); // Testing: I don't believe this event fires with polyfilled slots. } handleCounterClick(e) { @@ -279,7 +279,7 @@ export class CbpDropdown { End: l, }[key]; if (n !== undefined && key !== 'Tab') { - console.log('i',i,'l',l,'n',n) + //console.log('i',i,'l',l,'n',n) this.matchIndex = n; this.setCurrent( (this.filter && this.searchString) ? this.matches[n] : n, this.focusIndex); if (!this.filter) this.searchString=''; @@ -335,7 +335,6 @@ export class CbpDropdown { let matches=[]; this.dropdownItems.forEach( (item, index) => { const label=item.innerText.toLowerCase(); - console.log({label}); // does this item start with the character pressed? if (label.startsWith(letter)) { matches=[...matches, index]; @@ -384,7 +383,6 @@ export class CbpDropdown { } filterDropdownItems(matches){ - console.log('filterDropdownItems: ', {matches}); this.dropdownItems.forEach( (item, index) => { matches.includes(index) ? item.removeAttribute('hidden') : item.setAttribute('hidden',''); }); @@ -519,7 +517,6 @@ export class CbpDropdown { } componentWillRender() { - //console.log('Dropdown Component Will Render - how often is re-rendering happening?'); if (this.attachedButtonStart) this.attachedButtonStart.disabled=this.disabled || !this.dropdownItems.length; if (this.attachedButtonEnd) this.attachedButtonEnd.disabled=this.disabled || !this.dropdownItems.length; } diff --git a/packages/web-components/src/components/cbp-table/api-docs.mdx b/packages/web-components/src/components/cbp-table/api-docs.mdx new file mode 100644 index 00000000..7c112ee3 --- /dev/null +++ b/packages/web-components/src/components/cbp-table/api-docs.mdx @@ -0,0 +1,6 @@ +import { Meta, Markdown } from "@storybook/blocks"; +import Docs from './readme.md?raw'; + + + +{Docs} diff --git a/packages/web-components/src/components/cbp-table/cbp-table.scss b/packages/web-components/src/components/cbp-table/cbp-table.scss new file mode 100644 index 00000000..6057b6bc --- /dev/null +++ b/packages/web-components/src/components/cbp-table/cbp-table.scss @@ -0,0 +1,144 @@ +/** + * @prop --cbp-table-color: var(--cbp-color-text-darkest); + * @prop --cbp-table-color-dark: var(--cbp-color-text-lightest); + + * @prop --cbp-table-header-color-bg: var(--cbp-color-gray-cool-20); + * @prop --cbp-table-header-color-bg-dark: var(--cbp-color-gray-cool-60); + + * @prop --cbp-table-row-color-bg: var(--cbp-color-white); + * @prop --cbp-table-row-color-bg-dark: var(--cbp-color-gray-cool-70); + * @prop --cbp-table-row-color-bg-striped: var(--cbp-color-gray-cool-4); + * @prop --cbp-table-row-color-bg-striped-dark: var(--cbp-color-gray-cool-80); + * @prop --cbp-table-row-color-bg-hover: var(--cbp-color-gray-cool-10); + * @prop --cbp-table-row-color-bg-hover-dark: var(--cbp-color-gray-cool-90); + * @prop --cbp-table-row-color-bg-selected: var(--cbp-color-interactive-selected-light); + * @prop --cbp-table-row-color-bg-selected-dark: var(--cbp-color-interactive-selected-dark); + + * @prop --cbp-table-row-color-border: var(--cbp-color-gray-cool-30); + * @prop --cbp-table-row-color-border-dark: var(--cbp-color-gray-cool-50); + * @prop --cbp-table-row-border-size: var(--cbp-border-size-md); + */ +:root { + --cbp-table-color: var(--cbp-color-text-darkest); + --cbp-table-color-dark: var(--cbp-color-text-lightest); + + --cbp-table-header-color-bg: var(--cbp-color-gray-cool-20); + --cbp-table-header-color-bg-dark: var(--cbp-color-gray-cool-60); + + --cbp-table-row-color-bg: var(--cbp-color-white); + --cbp-table-row-color-bg-dark: var(--cbp-color-gray-cool-70); + --cbp-table-row-color-bg-striped: var(--cbp-color-gray-cool-4); + --cbp-table-row-color-bg-striped-dark: var(--cbp-color-gray-cool-80); + --cbp-table-row-color-bg-hover: var(--cbp-color-gray-cool-10); + --cbp-table-row-color-bg-hover-dark: var(--cbp-color-gray-cool-90); + --cbp-table-row-color-bg-selected: var(--cbp-color-interactive-selected-light); + --cbp-table-row-color-bg-selected-dark: var(--cbp-color-interactive-selected-dark); + + --cbp-table-row-color-border: var(--cbp-color-gray-cool-30); + --cbp-table-row-color-border-dark: var(--cbp-color-gray-cool-50); + --cbp-table-row-border-size: var(--cbp-border-size-md); +} + + +/* + * Dark Mode - display dark design based on mode or context + */ + [data-cbp-theme=light] cbp-table[context*=dark], + [data-cbp-theme=dark] cbp-table:not([context=dark-inverts]):not([context=light-always]){ + --cbp-table-color: var(--cbp-table-color-dark); + + --cbp-table-header-color: var(--cbp-table-header-color-dark); + --cbp-table-header-color-bg: var(--cbp-table-header-color-bg-dark); + + --cbp-table-row-color-bg: var(--cbp-table-row-color-bg-dark); + --cbp-table-row-color-bg-striped: var(--cbp-table-row-color-bg-striped-dark); + --cbp-table-row-color-bg-hover: var(--cbp-table-row-color-bg-hover-dark); + --cbp-table-row-color-bg-selected: var(--cbp-table-row-color-bg-selected-dark); + + --cbp-table-row-color-border: var(--cbp-table-row-color-border-dark); + } + +cbp-table { + display: block; + max-width: 100%; + overflow-x: auto; + + // Striped rows + &[striped=odd] { + tbody { + tr:nth-child(odd) { + background-color: var(--cbp-table-row-color-bg-striped); + } + } + } + + &[striped=even] { + tbody { + tr:nth-child(even) { + background-color: var(--cbp-table-row-color-bg-striped); + } + } + } + + + table { + margin: 0; + padding: 0; + max-width: 100%; + border-collapse: collapse; + + caption { + text-align: left; + font-size: var(--cbp-font-size-heading-xl); + line-height: var(--cbp-line-height-xl); + } + + th, td { + text-align: left; + padding: var(--cbp-space-3x) var(--cbp-space-4x); + + // TechDebt: may need to do these sorts of overrides for additional components inside of tables and reevaluate where the selectors belong. + &:has(input[type=checkbox]) { + padding-block: 0; + } + + cbp-checkbox { + --cbp-checkbox-min-height: 0; + --cbp-checkbox-margin: 0; + } + + } + + // thead, tfoot + + tbody { + tr { + background-color: var(--cbp-table-row-color-bg); + + &:has(input[type=checkbox]:checked) { + background-color: var(--cbp-table-row-color-bg-selected); + } + + &:hover td { + background-color: var(--cbp-table-row-color-bg-hover); + } + } + } + + tr { + border-bottom: solid var(--cbp-table-row-border-size) var(--cbp-table-row-color-border); + } + + th { + background-color: var(--cbp-table-header-color-bg); + font-weight: var(--cbp-font-weight-medium); + } + + td { + text-wrap: pretty; + } + + } + + +} diff --git a/packages/web-components/src/components/cbp-table/cbp-table.specs.mdx b/packages/web-components/src/components/cbp-table/cbp-table.specs.mdx new file mode 100644 index 00000000..50c5d9cb --- /dev/null +++ b/packages/web-components/src/components/cbp-table/cbp-table.specs.mdx @@ -0,0 +1,37 @@ +import { Meta } from '@storybook/addon-docs'; + + + +# cbp-table + +## Purpose + +The Table component is a wrapper component encapsulating design system styles for semantic HTML tables as well as applying progressive enhancements to the contained table. + +## Functional Requirements + +* The Table component contains the basic styles for semantic HTML tables slotted inside of it. +* The Table component allows for striped or unstriped table rows. +* The Typography component can also set a piece of content to act as a divider, either with a faux-underline (bottom-border) or as a shaded block. + +## Technical Specifications + +### User Interactions + +* The Table component is a wrapper, which may progressively enhance its contained/slotted table with different user interactions, such as: + * Row/column/cell highlighting on mouse hover. + * Sortable table columns via a column header control. (TODO) + * Selectable rows via a slotted checkbox. (application logic) + +### Responsiveness + +* TODO: There are a number of ways to display table data responsively, the "best" being largely contextual based on the data presented and its purpose. + +### Accessibility + +* The slotted table is expected to be marked up semantically, using `thead` and `tbody` elements and denoting headings with `th` elements (not `td`s). +* A table caption is mandatory, using the `caption` tag inside of the `table` element. If it is not desired to have a visible caption, place a `hidden` attribute on the `caption` element. + +### Additional Notes and Considerations + +* TODO: With potentially a large amount of DOM manipulation, evaluate if this is a good usecase for using ShadowDOM. diff --git a/packages/web-components/src/components/cbp-table/cbp-table.stories.tsx b/packages/web-components/src/components/cbp-table/cbp-table.stories.tsx new file mode 100644 index 00000000..6eb8efb6 --- /dev/null +++ b/packages/web-components/src/components/cbp-table/cbp-table.stories.tsx @@ -0,0 +1,108 @@ +export default { + title: 'Components/Table', + //tags: ['autodocs'], + argTypes: { + striped: { + control: 'select', + options: ['none', 'even', 'odd'], + }, + selectable: { + control: 'boolean' + }, + context : { + control: 'select', + options: [ "light-inverts", "light-always", "dark-inverts", "dark-always"] + }, + sx: { + description: 'Supports adding inline styles as an object of key-value pairs comprised of CSS properties and values. Values should reference design tokens when possible.', + control: 'object', + }, + }, +}; + + +function generateTableRows(data, selectable) { + const html = data.map( ({ row }, i) => { + const checkbox = `Select row ${i+1}` + let cells = row.map( ({td}) => { + return ` + ${td} + `; + }).join(''); + if (selectable) cells = checkbox + cells; + //return html.join(''); + return ` + + ${cells} + + `; + }); + return html.join(''); +} + + +const Template = ({ tableData, selectable, striped, context, sx }) => { + return ` + + + + + + ${ selectable ? `` : ''} + + + + + + + + ${generateTableRows(tableData, selectable)} + +
Table Caption
Select AllHeader 1Header 2Header 3Header 4
+ + `; +}; + +export const BasicTable = Template.bind({}); +BasicTable.args = { + striped: 'even', + tableData: [ + { + row: [ + {td: 'Cell Text'}, + {td: 'Cell Text'}, + {td: 'Cell Text'}, + {td: 'Cell Text'}, + ] + }, + { + row: [ + {td: 'Cell Text'}, + {td: 'Cell Text'}, + {td: 'Cell Text'}, + {td: 'Cell Text'}, + ] + }, + { + row: [ + {td: 'Cell Text'}, + {td: 'Cell Text'}, + {td: 'Cell Text'}, + {td: 'Cell Text'}, + ] + }, + { + row: [ + {td: 'Cell Text'}, + {td: 'Cell Text'}, + {td: 'Cell Text'}, + {td: 'Cell Text'}, + ] + }, + ] +} + diff --git a/packages/web-components/src/components/cbp-table/cbp-table.tsx b/packages/web-components/src/components/cbp-table/cbp-table.tsx new file mode 100644 index 00000000..2b92209e --- /dev/null +++ b/packages/web-components/src/components/cbp-table/cbp-table.tsx @@ -0,0 +1,44 @@ +import { Component, Prop, Element, Host, h } from '@stencil/core'; +import { setCSSProps } from '../../utils/utils'; + +@Component({ + tag: 'cbp-table', + styleUrl: 'cbp-table.scss', +}) +export class CbpTable { + + private caption: HTMLTableCaptionElement; + + @Element() host: HTMLElement; + + @Prop({ reflect: true }) striped: "odd" | "even"; + @Prop({ reflect: true }) sortable: boolean; + + /** Specifies the context of the component as it applies to the visual design and whether it inverts when light/dark mode is toggled. Default behavior is "light-inverts" and does not have to be specified. */ + @Prop({ reflect: true }) context: 'light-inverts' | 'light-always' | 'dark-inverts' | 'dark-always'; + + /** Supports adding inline styles as an object */ + @Prop() sx: any = {}; + + + + componentWillLoad() { + if (typeof this.sx == 'string') { + this.sx = JSON.parse(this.sx) || {}; + } + setCSSProps(this.host, Object.assign({}, this.sx)); + } + + componentDidLoad() { + this.caption = this.host.querySelector('caption'); + if (!this.caption) console.warn(`cbp-table: A caption tag is required for accessibility. If you don't want a visible caption, add a 'hidden' attribute to it.`); + } + + render() { + return ( + + + + ); + } +} diff --git a/packages/web-components/src/components/cbp-typography/cbp-typography.scss b/packages/web-components/src/components/cbp-typography/cbp-typography.scss index 01179abd..cd3f8a7d 100644 --- a/packages/web-components/src/components/cbp-typography/cbp-typography.scss +++ b/packages/web-components/src/components/cbp-typography/cbp-typography.scss @@ -53,6 +53,7 @@ cbp-typography { &[variant=heading-md] > *, h4, &[variant=heading-sm] > *, h5 { --cbp-typography-color: var(--cbp-typography-color-large-text); + //text-wrap: balance; // not at this time } @@ -155,6 +156,7 @@ cbp-typography { font-weight: var(--cbp-font-weight-regular); line-height: var(--cbp-space-5x); max-width: var(--cbp-line-length-longer); + text-wrap: pretty; } &[variant=subhead] > * {