Skip to content

Commit d199222

Browse files
authored
Merge pull request #164 from US-CBP/feature/tabs-darkmode
Feature/tabs darkmode
2 parents 4a1f4c4 + 71d70f8 commit d199222

File tree

7 files changed

+209
-48
lines changed

7 files changed

+209
-48
lines changed

packages/web-components/.storybook/theme.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { create } from '@storybook/theming/create';
22

33
export default create({
4-
//base: 'light',
4+
base: globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light', //'light',
55
brandTitle: 'CBP Design System | Web Components',
66
brandImage: '../assets/images/cbp-seal.svg',
77
brandTarget: '_self',

packages/web-components/src/components/cbp-button/cbp-button.scss

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
* @Prop --cbp-button-border-radius: var(--cbp-border-radius-soft);
1818
*/
1919

20-
/* Buttons get customized and overridden often within the design system and may benefit from a fully fleshed-out CSS API */
20+
/*
21+
* TechDebt: This dark-mode implementation is not up to speed with the latest techniques
22+
* Buttons get customized and overridden often within the design system and may benefit from a fully fleshed-out CSS API
23+
*/
2124
:root {
2225
--cbp-button-color-text: var(--cbp-color-text-lightest);
2326
--cbp-button-color-text-hover: var(--cbp-color-text-lightest);

packages/web-components/src/components/cbp-button/cbp-button.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ export class CbpButton {
6868
*/
6969
@Prop() accessibilityText: string;
7070

71-
/** @Internal Specifies that a button should not be keyboard navigable by setting its tabindex to -1. This property should only be used in very specific cases. */
72-
@Prop() pointerOnly: boolean;
71+
/* @Internal Specifies that a button should not be keyboard navigable by setting its tabindex to -1. This property should only be used in very specific cases. */
72+
//@Prop() pointerOnly: boolean;
7373

7474
/** Marks the rendered button/link in a disabled state when specified. */
7575
@Prop() disabled: boolean;
@@ -80,6 +80,7 @@ export class CbpButton {
8080
/** Supports adding inline styles as an object */
8181
@Prop() sx: any = {};
8282

83+
8384
/** A custom event emitted when the click event occurs for either a rendered button or anchor/link. */
8485
@Event() buttonClick!: EventEmitter;
8586
/** A custom event emitted when the component has completed loading and its internal lifecycles. */
@@ -128,7 +129,7 @@ export class CbpButton {
128129
let hostattrs = getElementAttrs(this.host);
129130
this.persistedAttrs = Object.fromEntries(
130131
Object.entries(hostattrs).filter(
131-
([key]) => ( key.includes('aria') || key == 'role' )
132+
([key]) => ( key.includes('aria-') || key == 'role' )
132133
)
133134
);
134135
}
@@ -190,7 +191,7 @@ export class CbpButton {
190191
<button
191192
{...this.persistedAttrs}
192193
{...attrs}
193-
tabindex={this.pointerOnly || this.disabled ? -1 : 0}
194+
//tabindex={this.pointerOnly || this.disabled ? -1 : 0}
194195
aria-label={this.accessibilityText}
195196
aria-pressed={pressed ? 'true' : null}
196197
aria-expanded={expanded ? 'true' : null}
@@ -209,7 +210,7 @@ export class CbpButton {
209210
<a
210211
{...this.persistedAttrs}
211212
{...attrs}
212-
tabindex={this.pointerOnly || this.disabled ? -1 : 0}
213+
//tabindex={this.pointerOnly || this.disabled ? -1 : 0}
213214
aria-label={this.accessibilityText}
214215
aria-pressed={pressed ? 'true' : null}
215216
aria-expanded={expanded ? 'true' : null}
Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,86 @@
1-
cbp-tab {
1+
/*
2+
* Dark Mode - display dark design based on mode or context
3+
* CSS API originates in parent cbp-tabs component; dark mode override happens here due to color=danger variant.
4+
*/
5+
[data-cbp-theme=light] cbp-tabs[context*=dark] cbp-tab,
6+
[data-cbp-theme=dark] cbp-tabs:not([context=dark-inverts]):not([context=light-always]) cbp-tab {
7+
--cbp-tab-color: var(--cbp-tab-color-dark);
8+
--cbp-tab-color-hover: var(--cbp-tab-color-hover-dark);
9+
--cbp-tab-color-focus: var(--cbp-tab-color-focus-dark);
10+
--cbp-tab-color-active: var(--cbp-tab-color-active-dark);
11+
--cbp-tab-color-selected: var(--cbp-tab-color-selected-dark);
212

3-
&[color=danger] {
4-
button {
5-
color: var(--cbp-color-danger-base);
13+
--cbp-tab-color-bg: var(--cbp-tab-color-bg-dark);
14+
--cbp-tab-color-bg-hover: var(--cbp-tab-color-bg-hover-dark);
15+
--cbp-tab-color-bg-focus: var(--cbp-tab-color-bg-focus-dark);
16+
--cbp-tab-color-bg-active: var(--cbp-tab-color-bg-active-dark);
17+
--cbp-tab-color-bg-selected: var(--cbp-tab-color-bg-selected-dark);
618

7-
&:hover:not([aria-selected='true']):not(:focus) {
8-
background-color: var(--cbp-color-danger-lighter);
9-
border-color: var(--cbp-color-danger-dark);
10-
}
11-
}
12-
}
19+
--cbp-tab-color-border: var(--cbp-tab-color-border-dark);
20+
--cbp-tab-color-border-hover: var(--cbp-tab-color-border-hover-dark);
21+
--cbp-tab-color-border-focus: var(--cbp-tab-color-border-focus-dark);
22+
--cbp-tab-color-border-active: var(--cbp-tab-color-border-active-dark);
23+
--cbp-tab-color-border-selected: var(--cbp-tab-color-border-selected-dark);
24+
}
1325

26+
cbp-tab {
1427
button {
1528
all: unset;
16-
color: var(--cbp-color-interactive-secondary-darker);
29+
color: var(--cbp-tab-color);
30+
background-color: var(--cbp-tab-color-bg);
1731
box-sizing: border-box;
1832
min-height: var(--cbp-space-14x);
1933
padding: 0 var(--cbp-space-3x);
20-
border-bottom: var(--cbp-border-size-xl) solid transparent;
2134
font-size: var(--cbp-font-size-heading-xs);
2235
font-weight: var(--cbp-font-weight-medium);
2336
white-space: nowrap;
37+
border-bottom: var(--cbp-border-size-xl) solid var(--cbp-tab-color-border);
38+
border-radius: var(--cbp-tab-border-radius);
39+
outline-width: var(--cbp-border-size-md);
40+
outline-style: none;
41+
outline-color: var(--cbp-button-color-outline-focus);
42+
outline-offset: calc(-1 * var(--cbp-space-1x));
2443

25-
&:hover:not([aria-selected='true']):not(:focus) {
26-
background-color: var(--cbp-color-interactive-secondary-lighter);
27-
border-bottom-color: var(--cbp-color-interactive-secondary-darker);
44+
// The selected tab should appear non-interactive on the surface because selecting it again won't do anything
45+
&:hover {
46+
--cbp-tab-color: var(--cbp-tab-color-hover);
47+
--cbp-tab-color-bg: var(--cbp-tab-color-bg-hover);
48+
--cbp-tab-color-border: var(--cbp-tab-color-border-hover);
2849
}
2950

3051
&[aria-selected='true'] {
31-
color: var(--cbp-color-interactive-active-dark);
32-
border-color: var(--cbp-color-interactive-active-dark);
52+
--cbp-tab-color: var(--cbp-tab-color-selected);
53+
--cbp-tab-color-bg: var(--cbp-tab-color-bg-selected);
54+
--cbp-tab-color-border: var(--cbp-tab-color-border-selected);
3355
font-style: italic;
3456
}
3557

3658
&:focus {
37-
color: var(--cbp-color-text-lightest);
38-
background: var(--cbp-color-interactive-focus-dark);
39-
border-color: var(--cbp-color-interactive-focus-dark);
40-
outline: var(--cbp-border-size-md) solid var(--cbp-color-white);
41-
outline-offset: calc(-1 * var(--cbp-space-1x));
59+
--cbp-tab-color: var(--cbp-tab-color-focus);
60+
--cbp-tab-color-bg: var(--cbp-tab-color-bg-focus);
61+
--cbp-tab-color-border: var(--cbp-tab-color-border-focus);
62+
outline-style: solid;
4263
}
4364

4465
&:active {
45-
color: var(--cbp-color-text-lightest);
46-
background-color: var(--cbp-color-interactive-active-dark);
66+
--cbp-tab-color: var(--cbp-tab-color-active);
67+
--cbp-tab-color-bg: var(--cbp-tab-color-bg-active);
68+
--cbp-tab-color-border: var(--cbp-tab-color-border-active);
4769
outline: none;
4870
}
4971
}
72+
73+
&[color=danger] {
74+
--cbp-tab-color: var(--cbp-color-danger-base);
75+
--cbp-tab-color-dark: var(--cbp-color-danger-light);
76+
77+
--cbp-tab-color-hover: var(--cbp-color-danger-base);
78+
--cbp-tab-color-hover-dark: var(--cbp-color-danger-lighter);
79+
80+
--cbp-tab-color-bg-hover: var(--cbp-color-danger-lighter);
81+
--cbp-tab-color-bg-hover-dark: var(--cbp-color-danger-darker);
82+
83+
--cbp-tab-color-border-hover: var(--cbp-color-danger-dark);
84+
--cbp-tab-color-border-hover-dark: var(--cbp-color-danger-lighter);
85+
}
5086
}

packages/web-components/src/components/cbp-tabs/cbp-tabs.scss

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,117 @@
1+
/**
2+
* @prop --cbp-tabs-color-border: var(--cbp-color-gray-cool-30);
3+
* @prop --cbp-tabs-color-border-dark: var(--cbp-color-gray-cool-60);
4+
5+
* @prop --cbp-tab-color: var(--cbp-color-interactive-secondary-darker);
6+
* @prop --cbp-tab-color-hover: var(--cbp-color-interactive-secondary-darker);
7+
* @prop --cbp-tab-color-focus: var(--cbp-color-text-lightest);
8+
* @prop --cbp-tab-color-active: var(--cbp-color-text-lightest);
9+
* @prop --cbp-tab-color-selected: var(--cbp-color-interactive-active-dark);
10+
11+
* @prop --cbp-tab-color-bg: transparent;
12+
* @prop --cbp-tab-color-bg-hover: var(--cbp-color-interactive-secondary-lighter);
13+
* @prop --cbp-tab-color-bg-focus: var(--cbp-color-interactive-focus-dark);
14+
* @prop --cbp-tab-color-bg-active: var(--cbp-color-interactive-active-dark);
15+
* @prop --cbp-tab-color-bg-selected: transparent;
16+
17+
* @prop --cbp-tab-color-border: transparent;
18+
* @prop --cbp-tab-color-border-hover: var(--cbp-color-interactive-secondary-lighter);
19+
* @prop --cbp-tab-color-border-focus: var(--cbp-color-interactive-focus-dark);
20+
* @prop --cbp-tab-color-border-active: var(--cbp-color-interactive-active-dark);
21+
* @prop --cbp-tab-color-border-selected: var(--cbp-color-interactive-active-dark);
22+
23+
* @prop --cbp-tab-color-dark: var(--cbp-color-interactive-secondary-lighter);
24+
* @prop --cbp-tab-color-hover-dark: var(--cbp-color-interactive-secondary-lighter);
25+
* @prop --cbp-tab-color-focus-dark: var(--cbp-color-text-darkest);
26+
* @prop --cbp-tab-color-active-dark: var(--cbp-color-text-darkest);
27+
* @prop --cbp-tab-color-selected-dark: var(--cbp-color-interactive-active-light);
28+
29+
* @prop --cbp-tab-color-bg-dark: transparent;
30+
* @prop --cbp-tab-color-bg-hover-dark: var(--cbp-color-interactive-secondary-darker);
31+
* @prop --cbp-tab-color-bg-focus-dark: var(--cbp-color-interactive-focus-light);
32+
* @prop --cbp-tab-color-bg-active-dark: var(--cbp-color-interactive-active-light);
33+
* @prop --cbp-tab-color-bg-selected-dark: transparent;
34+
35+
* @prop --cbp-tab-color-border-dark: transparent;
36+
* @prop --cbp-tab-color-border-hover-dark: var(--cbp-color-interactive-secondary-lighter);
37+
* @prop --cbp-tab-color-border-focus-dark: var(--cbp-color-interactive-focus-light);
38+
* @prop --cbp-tab-color-border-active-dark: var(--cbp-color-interactive-active-light);
39+
* @prop --cbp-tab-color-border-selected-dark: var(--cbp-color-interactive-active-light);
40+
41+
* @prop --cbp-tab-border-radius: 0;
42+
*/
43+
:root {
44+
45+
--cbp-tabs-color-border: var(--cbp-color-gray-cool-30);
46+
--cbp-tabs-color-border-dark: var(--cbp-color-gray-cool-60);
47+
48+
--cbp-tab-color: var(--cbp-color-interactive-secondary-darker);
49+
--cbp-tab-color-hover: var(--cbp-color-interactive-secondary-darker);
50+
--cbp-tab-color-focus: var(--cbp-color-text-lightest);
51+
--cbp-tab-color-active: var(--cbp-color-text-lightest);
52+
--cbp-tab-color-selected: var(--cbp-color-interactive-active-dark);
53+
54+
--cbp-tab-color-bg: transparent;
55+
--cbp-tab-color-bg-hover: var(--cbp-color-interactive-secondary-lighter);
56+
--cbp-tab-color-bg-focus: var(--cbp-color-interactive-focus-dark);
57+
--cbp-tab-color-bg-active: var(--cbp-color-interactive-active-dark);
58+
--cbp-tab-color-bg-selected: transparent;
59+
60+
--cbp-tab-color-border: transparent;
61+
--cbp-tab-color-border-hover: var(--cbp-color-interactive-secondary-lighter);
62+
--cbp-tab-color-border-focus: var(--cbp-color-interactive-focus-dark);
63+
--cbp-tab-color-border-active: var(--cbp-color-interactive-active-dark);
64+
--cbp-tab-color-border-selected: var(--cbp-color-interactive-active-dark);
65+
66+
--cbp-tab-color-dark: var(--cbp-color-interactive-secondary-lighter);
67+
--cbp-tab-color-hover-dark: var(--cbp-color-interactive-secondary-lighter);
68+
--cbp-tab-color-focus-dark: var(--cbp-color-text-darkest);
69+
--cbp-tab-color-active-dark: var(--cbp-color-text-darkest);
70+
--cbp-tab-color-selected-dark: var(--cbp-color-interactive-active-light);
71+
72+
--cbp-tab-color-bg-dark: transparent;
73+
--cbp-tab-color-bg-hover-dark: var(--cbp-color-interactive-secondary-darker);
74+
--cbp-tab-color-bg-focus-dark: var(--cbp-color-interactive-focus-light);
75+
--cbp-tab-color-bg-active-dark: var(--cbp-color-interactive-active-light);
76+
--cbp-tab-color-bg-selected-dark: transparent;
77+
78+
--cbp-tab-color-border-dark: transparent;
79+
--cbp-tab-color-border-hover-dark: var(--cbp-color-interactive-secondary-lighter);
80+
--cbp-tab-color-border-focus-dark: var(--cbp-color-interactive-focus-light);
81+
--cbp-tab-color-border-active-dark: var(--cbp-color-interactive-active-light);
82+
--cbp-tab-color-border-selected-dark: var(--cbp-color-interactive-active-light);
83+
84+
--cbp-tab-border-radius: 0;
85+
}
86+
87+
/*
88+
* Dark Mode - display dark design based on mode or context
89+
*
90+
*/
91+
[data-cbp-theme=light] cbp-tabs[context*=dark],
92+
[data-cbp-theme=dark] cbp-tabs:not([context=dark-inverts]):not([context=light-always]) {
93+
--cbp-tabs-color-border: var(--cbp-tabs-color-border-dark);
94+
}
95+
196
cbp-tabs {
297
display: flex;
398
max-width: 100%;
499
overflow: hidden;
5-
background: linear-gradient(to top, var(--cbp-color-gray-cool-30) var(--cbp-border-size-md), transparent var(--cbp-border-size-md));
100+
background: linear-gradient(to top, var(--cbp-tabs-color-border) var(--cbp-border-size-md), transparent var(--cbp-border-size-md));
6101
margin-bottom: var(--cbp-space-4x);
7102

8-
cbp-button {
103+
// overrides for responsive controls
104+
// TechDebt - dark colors CSS API not yet implemented in cbp-button
105+
cbp-button:not(#fakeId) {
9106
align-self: flex-start;
10-
--cbp-button-color-fill: var(--cbp-color-background-light) !important;
11-
--cbp-button-color-border: var(--cbp-color-gray-cool-30) !important;
107+
/* Restore this when button CSS API is fixed
108+
--cbp-button-color-text: var(--cbp-tab-color);
109+
--cbp-button-color-text-dark: var(--cbp-tab-color-dark);
110+
--cbp-button-color-fill: var(--cbp-color-background-light);
111+
--cbp-button-color-fill-dark: var(--cbp-color-background-dark);
112+
--cbp-button-color-border: var(--cbp-color-gray-cool-30);
113+
--cbp-button-color-border-dark: var(--cbp-color-gray-cool-60);
114+
*/
12115
--cbp-button-border-radius: 0;
13116
}
14117

packages/web-components/src/components/cbp-tabs/cbp-tabs.stories.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export default {
1010
accessibilityText: {
1111
control: 'text',
1212
},
13+
context : {
14+
control: 'select',
15+
options: [ "light-inverts", "light-always", "dark-inverts", "dark-always"]
16+
},
1317
sx: {
1418
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.',
1519
control: 'object',
@@ -43,10 +47,11 @@ function createTabPanels(tabs) {
4347
return html.join('');
4448
}
4549

46-
const Template = ({ tabs, accessibilityText, sx }) => {
50+
const Template = ({ tabs, accessibilityText, context, sx }) => {
4751
return `
4852
<cbp-tabs
4953
${accessibilityText ? `accessibility-text="${accessibilityText}"` : ''}
54+
${context && context != 'light-inverts' ? `context=${context}` : ''}
5055
${sx ? `sx=${JSON.stringify(sx)}` : ''}
5156
>
5257
${createTabs(tabs)}

packages/web-components/src/components/cbp-tabs/cbp-tabs.tsx

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export class CbpTabs {
2323
/** The accessible label of the tablist. Required unless `aria-labelledby` is specified on the host tag directly. */
2424
@Prop() accessibilityText: string;
2525

26+
/** 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. */
27+
@Prop({ reflect: true }) context: 'light-inverts' | 'light-always' | 'dark-inverts' | 'dark-always';
28+
2629
/** Supports adding inline styles as an object */
2730
@Prop() sx: any = {};
2831

@@ -146,18 +149,23 @@ export class CbpTabs {
146149
}}
147150
>
148151
<cbp-button
149-
type="button"
150152
color="secondary"
151153
fill="outline"
152154
variant="square"
153-
pointerOnly={true}
154-
aria-label="Previous Tab"
155-
width='3.5rem'
156-
height='3.5rem'
155+
width="3.5rem"
156+
height="3.5rem"
157+
context={this.context}
157158
onClick={ () => this.responsiveNav('previous')}
158159
ref={el => (this.previousControl = el)}
159160
>
160-
<cbp-icon name="chevron-right" size="var(--cbp-space-5x)" rotate={180}></cbp-icon>
161+
<button
162+
type="button"
163+
tabindex="-1"
164+
aria-label="Previous Tab"
165+
slot="cbp-button-custom"
166+
>
167+
<cbp-icon name="chevron-right" size="var(--cbp-space-5x)" rotate={180}></cbp-icon>
168+
</button>
161169
</cbp-button>
162170

163171
<div
@@ -168,18 +176,23 @@ export class CbpTabs {
168176
</div>
169177

170178
<cbp-button
171-
type="button"
172179
color="secondary"
173180
fill="outline"
174181
variant="square"
175-
pointerOnly={true}
176-
aria-label="Next Tab"
177-
width='3.5rem'
178-
height='3.5rem'
182+
width="3.5rem"
183+
height="3.5rem"
184+
context={this.context}
179185
onClick={ () => this.responsiveNav('next')}
180186
ref={el => (this.nextControl = el)}
181187
>
182-
<cbp-icon name="chevron-right" size="var(--cbp-space-5x)"></cbp-icon>
188+
<button
189+
type="button"
190+
tabindex="-1"
191+
aria-label="Next Tab"
192+
slot="cbp-button-custom"
193+
>
194+
<cbp-icon name="chevron-right" size="var(--cbp-space-5x)"></cbp-icon>
195+
</button>
183196
</cbp-button>
184197
</Host>
185198
);

0 commit comments

Comments
 (0)