|
| 1 | +--- |
| 2 | +id: Compass |
| 3 | +section: AI |
| 4 | +source: Development-guide |
| 5 | +subsection: Generative UIs |
| 6 | +--- |
| 7 | + |
| 8 | +## Structural patterns |
| 9 | +When building generative UI layouts using the Compass components, there are several common patterns to consider during your development process. |
| 10 | + |
| 11 | +### Transparent containers |
| 12 | +Transparent containers create a modern glass look and feel. To enable transparency on a container, apply `pf-v6-theme-glass` to the `html` element class list, similar to the approach for enabling the dark mode theme. |
| 13 | + |
| 14 | +Within Compass layouts, elements with a transparent glass background and rounded borders should be wrapped in a `<CompassPanel>` (some Compass components do this automatically). You can adjust various options of the `<CompassPanel>` to adapt its padding and behavior to fit the context of your use case. |
| 15 | + |
| 16 | +To prevent styling issues, do not nest `<CompassPanel>` components while using the glass effects. |
| 17 | + |
| 18 | +### Header |
| 19 | +You can add a header to a Compass layout via the `<CompassHeader>` component. `<CompassHeader>` constructs 3 sections, which can contain any custom content, but will typically include the following: |
| 20 | +- **Logo:** Contains a [`<Brand>`](/components/brand) or other image or SVG to represent a product logo. |
| 21 | +- **Profile:** Contains a [`<Dropdown>`](/components/menus/dropdown) with a user [`<Avatar>`](/components/avatar) displayed in the `<MenuToggle>`. |
| 22 | +- **Nav:** Contains a `<CompassNavContent>` component, which usually contains `<CompassNavHome>`, `<CompassNavMain>`, and `<CompassNavSearch>` components as shown in the following code example. |
| 23 | + - `<CompassNavHome>` and `<CompassNavSearch>` are optional, but are opinionated components that will automatically provide a home and search button. |
| 24 | + - `<CompassNavMain>` will usually contain a [`<Tabs>`](/components/tabs) component that uses the `isNav` flag to enable a top-level navigation styles. |
| 25 | +#### Code example |
| 26 | + |
| 27 | +``` |
| 28 | +<CompassNavContent> |
| 29 | + <CompassNavHome onClick={() => console.log('Home')} /> |
| 30 | + <CompassNavMain> |
| 31 | + <Tabs isNav /> |
| 32 | + </CompassNavMain> |
| 33 | + <CompassNavSearch onClick={() => console.log('Search')} /> |
| 34 | +</CompassNavContent> |
| 35 | +``` |
| 36 | + |
| 37 | +### Sidebars |
| 38 | +There are 2 vertical sidebars in a Compass layout: 1 at the start of the page and another at the end. There are no specific helper components for these sections, so they're usually created by passing a `<CompassPanel>` that contains an [`<ActionList>`](/components/action-list) with the `isVertical` flag. |
| 39 | + |
| 40 | +### Docked navigation |
| 41 | +As an alternative to the above header and sidebars, a docked navigation may be used to organize navigation elements into a single anchored sidebar. You can add a docked nav to the Compass layout via the `dock` property. |
| 42 | + |
| 43 | +A docked nav will typically contain a [`<Masthead>`](/components/masthead) with the "docked" `variant` type. This masthead is formed similarly to a typical `Page` masthead, except `Toolbar` should be passed the `isVertical` flag, and `Nav` should also specify its `variant` as "docked". |
| 44 | + |
| 45 | +### Footer |
| 46 | +There are 2 methods of adding a footer to a compass layout. |
| 47 | + |
| 48 | +In both methods, the footer content will remain the same, typically containing a [ChatBot `<MessageBar>`](/extensions/chatbot/messages/), wrapped in a `<CompassPanel>` and `<CompassMessageBar>` component: |
| 49 | + |
| 50 | +``` |
| 51 | + <CompassMessageBar> |
| 52 | + <CompassPanel isPill hasNoPadding hasNoBorder> |
| 53 | + <MessageBar /> |
| 54 | + <div aria-live="polite" className="pf-v6-screen-reader"> |
| 55 | + // aria anouncments for message bar's state updates |
| 56 | + </div> |
| 57 | + </CompassPanel> |
| 58 | +</CompassMessageBar> |
| 59 | +``` |
| 60 | + |
| 61 | +1. Via the `footer` prop. |
| 62 | + |
| 63 | + To create a footer that will span the entire viewport width, pass content to the `footer` prop of the `<Compass>` component. If your footer content is expected to change in height (like `<MessageBar>` content that expands), the sidebars will adjust their positioning to align. If this behavior is not desired, don't use the `footer` prop and follow the second method. |
| 64 | + |
| 65 | +2. Via the `<CompassMainFooter>` components. |
| 66 | + |
| 67 | + To allow the sidebars to extend through the bottom of the viewport, preventing them from resizing based on changes to the footer container height, wrap footer content in `<CompassMainFooter>` and pass it to the `<Compass>` component as part of the `main` content. |
| 68 | + |
| 69 | +### Main content |
| 70 | +The main content in a Compass layout includes the generated information displays. Main content fills the center of the viewport and typically consists of a `<Hero>` or `<CompassMainHeader>`, a `<CompassContent>`, and an optional `<CompassMainFooter>`. |
| 71 | + |
| 72 | +Often, the main section will contain a `<CompassMainHeader>` and `<CompassContent>` with a `<CompassPanel>` child containing the primary page content. |
| 73 | + |
| 74 | +When making [a dashboard view](/patterns/dashboard), use a `<Hero>` instead of a `<CompassMainHeader>`. Instead of having a single `<CompassPanel>` within a `<CompassContent>`, each individual dashboard item should be wrapped in a `<CompassPanel>`. For example, in a dashboard with many content cards, each `<Card>` (with `isPlain` flag) should be wrapped by a `<CompassPanel>` inside of a [`<Grid>`](/foundations-and-styles/layouts/grid). |
| 75 | + |
| 76 | +## CSS customization |
| 77 | + |
| 78 | +Our [design token system](/foundations-and-styles/design-tokens/overview) allows you to further customize the look and feel of a Compass layout. |
| 79 | + |
| 80 | +For example, the following CSS mixin utilizes several global token overrides to better match the Red Hat Design System look and feel. |
| 81 | + |
| 82 | +``` |
| 83 | +@mixin pf-v6-tokens { |
| 84 | + // brand tokens |
| 85 | + --pf-t--global--color--brand--100: var(--pf-t--color--red--40); |
| 86 | + --pf-t--global--color--brand--200: var(--pf-t--color--red--50); |
| 87 | + --pf-t--global--color--brand--300: var(--pf-t--color--red--60); |
| 88 | + --pf-t--global--color--brand--400: var(--pf-t--color--red--70); |
| 89 | + --pf-t--global--color--brand--500: var(--pf-t--color--red--80); |
| 90 | +
|
| 91 | + // dark brand tokens |
| 92 | + --pf-t--global--dark--color--brand--100: var(--pf-t--color--red--30); |
| 93 | + --pf-t--global--dark--color--brand--200: var(--pf-t--color--red--20); |
| 94 | + --pf-t--global--dark--color--brand--300: var(--pf-t--color--red--10); |
| 95 | + |
| 96 | + // font size |
| 97 | + --pf-t--global--font--size--900: 2.5rem; |
| 98 | + --pf-t--global--font--size--1000: 3rem; |
| 99 | +
|
| 100 | + // bumped by one |
| 101 | + --pf-t--global--font--size--2xl: var(--pf-t--global--font--size--700); |
| 102 | + --pf-t--global--font--size--3xl: var(--pf-t--global--font--size--800); |
| 103 | + --pf-t--global--font--size--4xl: var(--pf-t--global--font--size--900); |
| 104 | + --pf-t--global--font--size--lg: var(--pf-t--global--font--size--500); |
| 105 | + --pf-t--global--font--size--md: var(--pf-t--global--font--size--400); |
| 106 | + --pf-t--global--font--size--sm: var(--pf-t--global--font--size--300); |
| 107 | + --pf-t--global--font--size--xl: var(--pf-t--global--font--size--600); |
| 108 | + --pf-t--global--font--size--xs: var(--pf-t--global--font--size--200); |
| 109 | + |
| 110 | + // icon sizes |
| 111 | + --pf-t--global--icon--size--100: 1rem; |
| 112 | + --pf-t--global--icon--size--200: 1.5rem; |
| 113 | + --pf-t--global--icon--size--250: 2rem; |
| 114 | + --pf-t--global--icon--size--300: 2.5rem; |
| 115 | + --pf-t--global--icon--size--400: 6rem; |
| 116 | + --pf-t--global--icon--size--500: 8rem; |
| 117 | +
|
| 118 | + // spacing |
| 119 | + --pf-t--global--spacer--action--horizontal--compact: var(--pf-t--global--spacer--lg); |
| 120 | + --pf-t--global--spacer--action--horizontal--default: var(--pf-t--global--spacer--xl); |
| 121 | + --pf-t--global--spacer--action--horizontal--plain--compact: var(--pf-t--global--spacer--sm); |
| 122 | + --pf-t--global--spacer--action--horizontal--plain--default: var(--pf-t--global--spacer--md); |
| 123 | + --pf-t--global--spacer--action--horizontal--spacious: var(--pf-t--global--spacer--2xl); |
| 124 | + --pf-t--global--spacer--control--horizontal--compact: var(--pf-t--global--spacer--md); |
| 125 | + --pf-t--global--spacer--control--horizontal--default: var(--pf-t--global--spacer--xl); |
| 126 | + --pf-t--global--spacer--control--horizontal--plain: var(--pf-t--global--spacer--md); |
| 127 | + --pf-t--global--spacer--control--horizontal--spacious: var(--pf-t--global--spacer--xl); |
| 128 | + --pf-t--global--spacer--control--vertical--compact: var(--pf-t--global--spacer--sm); |
| 129 | + --pf-t--global--spacer--control--vertical--default: var(--pf-t--global--spacer--md); |
| 130 | + --pf-t--global--spacer--control--vertical--plain: var(--pf-t--global--spacer--md); |
| 131 | + --pf-t--global--spacer--control--vertical--spacious: var(--pf-t--global--spacer--lg); |
| 132 | + --pf-t--global--spacer--gap--action-to-action--default: var(--pf-t--global--spacer--lg); |
| 133 | + --pf-t--global--spacer--gap--action-to-action--plain: var(--pf-t--global--spacer--sm); |
| 134 | + --pf-t--global--spacer--gap--control-to-control--default: var(--pf-t--global--spacer--sm); |
| 135 | + --pf-t--global--spacer--gap--group--horizontal: var(--pf-t--global--spacer--lg); |
| 136 | + --pf-t--global--spacer--gap--group--vertical: var(--pf-t--global--spacer--md); |
| 137 | + --pf-t--global--spacer--gap--group-to-group--horizontal--compact: var(--pf-t--global--spacer--md); |
| 138 | + --pf-t--global--spacer--gap--group-to-group--horizontal--default: var(--pf-t--global--spacer--3xl); |
| 139 | + --pf-t--global--spacer--gap--group-to-group--vertical--compact: var(--pf-t--global--spacer--lg); |
| 140 | + --pf-t--global--spacer--gap--group-to-group--vertical--default: var(--pf-t--global--spacer--xl); |
| 141 | + --pf-t--global--spacer--gap--text-to-element--compact: var(--pf-t--global--spacer--sm); |
| 142 | + --pf-t--global--spacer--gap--text-to-element--default: var(--pf-t--global--spacer--md); |
| 143 | + --pf-t--global--spacer--gutter--default: var(--pf-t--global--spacer--lg); |
| 144 | + --pf-t--global--spacer--inset--page-chrome: var(--pf-t--global--spacer--xl); |
| 145 | +
|
| 146 | + // Glass theme tokens |
| 147 | + --pf-t--global--light--glass--background--color--glass--color: var(--pf-t--global--color--brand--500); |
| 148 | + --pf-t--global--light--glass--background--color--glass--filter: blur(12.5px); |
| 149 | + --pf-t--global--light--glass--background--color--glass--opacity: 10%; |
| 150 | + --pf-t--global--light--glass--background--color--glass--default: color-mix(in srgb, var(--pf-t--global--light--glass--background--color--glass--color) var(--pf-t--global--light--glass--background--color--glass--opacity), transparent ); |
| 151 | + --pf-t--global--dark--glass--background--color--glass--color: var(--pf-t--global--color--brand--500); |
| 152 | + --pf-t--global--dark--glass--background--color--glass--filter: blur(12.5px); |
| 153 | + --pf-t--global--dark--glass--background--color--glass--opacity: 65%; |
| 154 | + --pf-t--global--dark--glass--background--color--glass--default: color-mix(in srgb, var(--pf-t--global--dark--glass--background--color--glass--color) var(--pf-t--global--dark--glass--background--color--glass--opacity), transparent ); |
| 155 | +
|
| 156 | + // Message bar tokens |
| 157 | + --pf-v6-c-compass__message-bar--Width: 600px; |
| 158 | + --pf-v6-c-compass__message-bar--MinWidth: 450px; |
| 159 | + --pf-v6-c-compass__message-bar--MaxWidth: 900px; |
| 160 | +
|
| 161 | + // Thinking tokens |
| 162 | + --pf-v6-global--thinking--BoxShadow--Spread: 5px; |
| 163 | + --pf-v6-global--thinking--BoxShadow--Color--Start-Start: var(--pf-t--global--color--brand--100); |
| 164 | + --pf-v6-global--thinking--BoxShadow--Color--Start-End: var(--pf-t--color--white); |
| 165 | + --pf-v6-global--thinking--BoxShadow--Color--End-Start: var(--pf-t--global--color--brand--500); |
| 166 | + --pf-v6-global--thinking--BoxShadow--Color--End-End: var(--pf-t--global--color--brand--300); |
| 167 | + --pf-v6-global--thinking-active--BoxShadow--Spread: 10px; |
| 168 | +
|
| 169 | + .pf-v6-m-ai-indicator { |
| 170 | + &::before { |
| 171 | + background: linear-gradient(to right, #f56e6e 0%, var(--pf-t--global--color--brand--300) 65%, var(--pf-t--global--color--brand--500) 100%) |
| 172 | + border-box; |
| 173 | + } |
| 174 | + } |
| 175 | +} |
| 176 | +``` |
0 commit comments