Skip to content

Commit

Permalink
Merge pull request #145 from US-CBP/feature/drawer-darkmode
Browse files Browse the repository at this point in the history
Feature/drawer darkmode
  • Loading branch information
dgibson666 authored Jun 7, 2024
2 parents 89f4b7f + 8626a69 commit ac9309c
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 34 deletions.
27 changes: 21 additions & 6 deletions packages/web-components/src/components/cbp-drawer/cbp-drawer.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
/*
*
*/
:root {
//--cbp-drawer-color: ;
--cbp-drawer-color-bg: var(--cbp-color-white);
}

/*
* Dark Mode - display dark design based on mode or context
*/
[data-cbp-theme=light] cbp-drawer[context*=dark],
[data-cbp-theme=dark] cbp-drawer:not([context=dark-inverts]):not([context=light-always]) {
--cbp-drawer-color-bg: var(--cbp-color-gray-cool-70);
}


cbp-drawer {
all: unset;
display: none;
}

cbp-drawer[open] {
--cbp-panel-border-radius: 0;
--cbp-panel-border-width: 0;
}

cbp-drawer[open] {
display: block;
position: fixed;
top: 0;
Expand All @@ -15,7 +31,7 @@ cbp-drawer[open] {
left: 0;
z-index: calc(var(--cbp-z-index-level-top) - 1);
overflow-y: auto;
background-color: rgba(0, 0, 0, 0.3);
background-color: rgba(0, 0, 0, 0.3); // backdrop

.cbp-drawer__content {
display: flex;
Expand All @@ -35,10 +51,9 @@ cbp-drawer[open] {

transition: 0.5s;
height: 100%;
background-color: var(--cbp-color-white);
background-color: var(--cbp-drawer-color-bg);

.cbp-drawer__close-button {
--cbp-button-color-text: var(--cbp-color-interactive-secondary-lighter);
position: absolute;
right: 1rem;
top: 1em;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@ export default {
description: 'A unique `id` applied to the drawer and referenced by the control.',
control: 'text',
},
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',
},
},
};

const Template = ({ position, open, uid, accessibilityText, sx }) => {
const Template = ({ position, open, uid, accessibilityText, context, sx }) => {
return `
<cbp-button
type="button"
Expand All @@ -38,12 +42,14 @@ const Template = ({ position, open, uid, accessibilityText, sx }) => {
${position ? `position=${position}` : ''}
${open ? `open=${open}` : ''}
${accessibilityText ? `accessibility-text=${accessibilityText}` : ''}
${context && context != 'light-inverts' ? `context=${context}` : ''}
${sx ? `sx=${JSON.stringify(sx)}` : ''}
${uid ? `uid=${uid}` : ''}
>
<cbp-panel
aria-labelledby="panelheader"
role="complementary"
${context && context != 'light-inverts' ? `context=${context}` : ''}
>
<cbp-typography
slot="cbp-panel-header"
Expand All @@ -63,19 +69,52 @@ export const Drawer = Template.bind({});
Drawer.args = {
position: 'left',
uid: 'drawer',
context: 'light-always'
};

/*
<div class="cbp-drawer__header">
<div>
<cbp-icon name="filter"></cbp-icon>
<h3>Filter</h3>
</div>
<cbp-button aria-label="Close">
<cbp-icon name="circle-xmark"></cbp-icon>
</cbp-button>
</div>

<div class="cbp-drawer__content"></div>
const UserPreferencesTemplate = ({ position, open, uid, accessibilityText, context, sx }) => {
return `
<cbp-button
type="button"
color="secondary"
accessibility-text="Open Drawer"
target-prop="open"
controls=${uid}
>
<cbp-icon name="bars"></cbp-icon>
</cbp-button>
<cbp-drawer
${position ? `position=${position}` : ''}
${open ? `open=${open}` : ''}
${accessibilityText ? `accessibility-text=${accessibilityText}` : ''}
${context && context != 'light-inverts' ? `context=${context}` : ''}
${sx ? `sx=${JSON.stringify(sx)}` : ''}
${uid ? `uid=${uid}` : ''}
>
<cbp-panel
aria-labelledby="panelheader"
role="complementary"
${context && context != 'light-inverts' ? `context=${context}` : ''}
>
<cbp-typography
slot="cbp-panel-header"
tag="h3"
variant="heading-lg"
id="panelheader"
>
Sidebar Header
</cbp-typography>
<p>Sidebar Content</p>
</cbp-panel>
</cbp-drawer>
`;
};

*/
export const UserPreferences = UserPreferencesTemplate.bind({});
UserPreferences.args = {
position: 'right',
uid: 'drawer',
context: 'dark-always'
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ export class CbpDrawer {
/** Creates an accessible label for the drawer (dialog). */
@Prop() accessibilityText: string;

/** 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 = {};


/** Custom event fired when the drawer is opened. */
@Event() drawerOpen!: EventEmitter;
/** Custom event fired when the drawer is closed. */
Expand Down Expand Up @@ -117,9 +119,10 @@ export class CbpDrawer {
type="button"
color="secondary"
fill="ghost"
accessibility-text="Close"
target-prop="open"
accessibilityText="Close"
targetProp="open"
controls={this.uid}
context="dark-always"
>
<cbp-icon name="circle-xmark"></cbp-icon>
</cbp-button>
Expand Down
28 changes: 21 additions & 7 deletions packages/web-components/src/components/cbp-panel/cbp-panel.scss
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
/**
* @Prop --cbp-panel-header-color: var(--cbp-color-text-lighter)
* @Prop --cbp-panel-header-color-background: var(--cbp-color-base-neutral-dark);
* @Prop --cbp-panel-header-color-bg: var(--cbp-color-base-neutral-dark);
* @Prop --cbp-panel-header-color-bottom-border: var(--cbp-color-gray-cool-70);
* @Prop --cbp-panel-content-color-background: var(--cbp-color-white);
* @Prop --cbp-panel-content-color-bg: var(--cbp-color-white);
* @Prop --cbp-panel-content-color-border: var(--cbp-color-gray-cool-10);
* @Prop --cbp-panel-border-radius: var(--cbp-border-radius-soft);
* @Prop --cbp-panel-border-width: var(--cbp-border-size-md);
*/
:root {
--cbp-panel-header-color: var(--cbp-color-text-lighter);
--cbp-panel-header-color-background: var(--cbp-color-base-neutral-dark);
--cbp-panel-header-color-bg: var(--cbp-color-base-neutral-dark);
--cbp-panel-header-color-bottom-border: var(--cbp-color-gray-cool-70);
//--cbp-panel-content-color: var(--cbp-color-text-lighter);
--cbp-panel-content-color-background: var(--cbp-color-white);
--cbp-panel-content-color: var(--cbp-color-text-darkest);
--cbp-panel-content-color-bg: var(--cbp-color-white);
--cbp-panel-content-color-border: var(--cbp-color-gray-cool-10);
--cbp-panel-border-radius: var(--cbp-border-radius-soft);
--cbp-panel-border-width: var(--cbp-border-size-md);
}

/*
* Dark Mode - display dark design based on mode or context
*/
[data-cbp-theme=light] cbp-panel[context*=dark],
[data-cbp-theme=dark] cbp-panel:not([context=dark-inverts]):not([context=light-always]) {
--cbp-panel-header-color: var(--cbp-color-text-lighter);
--cbp-panel-header-color-bg: var(--cbp-color-branding-dhs-blue);
--cbp-panel-header-color-bottom-border: var(--cbp-color-gray-cool-50);
--cbp-panel-content-color: var(--cbp-color-text-lightest);
--cbp-panel-content-color-bg: var(--cbp-color-gray-cool-70);
--cbp-panel-content-color-border: var(--cbp-color-gray-cool-60);
}

cbp-panel {
display: block;

.cbp-panel__header {
color: var(--cbp-panel-header-color);
background-color: var(--cbp-panel-header-color-background);
background-color: var(--cbp-panel-header-color-bg);
padding: var(--cbp-space-5x);
border-bottom: solid var(--cbp-border-size-xl) var(--cbp-panel-header-color-bottom-border);
border-radius: var(--cbp-panel-border-radius) var(--cbp-panel-border-radius) 0 0;
Expand All @@ -37,7 +50,8 @@ cbp-panel {
}

.cbp-panel__content {
background-color: var(--cbp-panel-content-color-background);
color: var(--cbp-panel-content-color);
background-color: var(--cbp-panel-content-color-bg);
padding: var(--cbp-space-5x);
border-style: solid;
border-color: var(--cbp-panel-content-color-border);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,26 @@ export default {
description: 'An accessible label may be provided if the text within the header is not sufficiently descriptive or unique. In such a case, omit the heading `id`.',
control: 'text',
},
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',
},
},
};

const PanelTemplate = ({ role, headingLevel, header, headerId, content, ariaLabel, showIcon }) =>
const PanelTemplate = ({ role, headingLevel, header, headerId, content, ariaLabel, showIcon, context, sx }) =>
`
<cbp-panel
${headerId ? `aria-labelledby="${headerId}"` : ''}
${role != 'none' ? `role="${role}"` : ''}
${ariaLabel ? `aria-label="${ariaLabel}"` : ''}
${headerId ? `aria-labelledby="${headerId}"` : ''}
${context && context != 'light-inverts' ? `context=${context}` : ''}
${sx ? `sx=${JSON.stringify(sx)}` : ''}
>
<cbp-typography
slot="cbp-panel-header"
Expand Down
21 changes: 20 additions & 1 deletion packages/web-components/src/components/cbp-panel/cbp-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import { Component, Host, h } from '@stencil/core';
import { Component, Prop, Element, Host, h } from '@stencil/core';
import { setCSSProps } from '../../utils/utils';

@Component({
tag: 'cbp-panel',
styleUrl: 'cbp-panel.scss'
})
export class CbpPanel {

@Element() host: HTMLElement;

/** 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, {
...this.sx,
});
}


render() {
return (
<Host>
Expand Down
4 changes: 2 additions & 2 deletions packages/web-components/src/stories/darkmode.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Interactive states may also invert from light to dark and vice versa (on hover,

* How do these selectors for dark mode and dark context affect CSS specificity and the ability to override styles with the CSS API?
* The specificity for the selectors (below) is 0,2,1 and 0,3,1 respectively.
* Investigate using `@layer` to manage specificity at a higher level.
* Investigate using `@layer` to manage specificity at a higher level. (too new - support still limited)
* Investigate using `:where()` to reduce specificity.
* How does this interact with styles specified via the sx property? Colors specified via sx would likely only apply to one mode unless the CSS API contains root-level variables for both modes.

Expand Down Expand Up @@ -74,7 +74,7 @@ The following CSS selectors are used for defining the component's dark mode desi

## Results

The specificity of the above "dark mode" selectors is 0,2,1 and 0,3,1 respectively. While this has not been problematic as yet, these values are a little high and I would like to further investigate using `where()` and/or `@layer` to manage the specificity.
The specificity of the above "dark mode" selectors is 0,2,1 and 0,3,1 respectively. While this has not been problematic as yet, these values are a little high and I would like to further investigate using `:where()` and/or `@layer` to manage the specificity.

Another important factor in managing specificity is to make sure each component has color variables set at the `:root` level (component CSS API). This makes overriding them with the dark mode selectors (or within another component using the component CSS API) much simpler than if colors are set directly in deeper selectors. This includes (especially) states such as hover, focus, active, etc., as those can be especially difficult and tedious to override with their high specificity.

Expand Down
5 changes: 4 additions & 1 deletion packages/web-components/src/stories/templates.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ const InternalTemplate = ({ isLoggedIn, username }) => {
<cbp-skip-nav></cbp-skip-nav>
<header>
<cbp-universal-header>
<cbp-universal-header
logo-src-lg="./assets/images/cbp-header-logo.svg"
logo-src-sm="./assets/images/cbp-seal.svg"
>
<ul>
${ isLoggedIn
? `
Expand Down

0 comments on commit ac9309c

Please sign in to comment.