Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify colors and add styles docs #2069

Merged
merged 22 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9a8a834
Add styles doc written during react-uswds migration
haworku Nov 13, 2023
0fb4916
Add MCR color scheme
haworku Nov 13, 2023
3d6da8d
MCR colors setup. Add documentation tweaks
haworku Nov 14, 2023
ee13554
yarn build is working
haworku Dec 18, 2023
7bccc83
Merge remote-tracking branch 'origin/main' into styles-docs
haworku Dec 18, 2023
1bf796f
Cleanup - go through landing page, form pages, settings pages
haworku Dec 18, 2023
d2b9ec1
Move back to @import usage to get app deploying
haworku Dec 19, 2023
8fbef5a
link to custom not colors
haworku Dec 19, 2023
52781e5
Fix state dashboard styling
haworku Dec 19, 2023
b774420
Pull out overrides as seperate module, distinct from custom
haworku Dec 26, 2023
2b21df2
Move towards @use in each module.scss file. Document that @import …
haworku Dec 26, 2023
812bd08
Add stylelint and run scss linter
haworku Dec 27, 2023
87f540d
Merge remote-tracking branch 'origin/main' into styles-docs
haworku Dec 27, 2023
d5c7f3e
Run yarn at root
haworku Dec 28, 2023
609a3a8
Fix yarn build error by importing sass for sass commands
haworku Dec 28, 2023
5f12aa6
Stylelint updates - run on precommit, disallow hex variables for color
haworku Dec 28, 2023
851f227
Storybook changes. Address @kelseylistrom comments.
haworku Dec 28, 2023
2caaa7b
Cleanup yarn.lock
haworku Dec 28, 2023
6fd991f
Cleanup Colors.module.scss
haworku Dec 29, 2023
fd6fe27
Address @pearl-truss code review
haworku Jan 4, 2024
3354b84
Address @pearl-truss code review. Also get lint-staged working and ya…
haworku Jan 5, 2024
b3f78b6
Merge remote-tracking branch 'origin/main' into styles-docs
haworku Jan 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/bash
. "$(dirname "$0")/_/husky.sh"

# In a continuing effort to print errors when they are needed, we check for
# all pre-commit dependency requirements here.
# In a continuing effort to print errors when they are needed, we check for
# all pre-commit dependency requirements here.
requirements=("protolint" "shellcheck" "detect-secrets")

for req in "${requirements[@]}"; do
Expand Down
105 changes: 105 additions & 0 deletions docs/technical-design/howto-style-css-scss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# How to style with CSS and SCSS

## Colors

MC-Review has an application specific color palette written in SCSS. All color variables in the palette are prefixed with `$mcr-`. The palette is viewable in Storybook as well (`Global/Colors`). Anytime you need a color prefer the variable - e.g. $mcr-gold-base. Avoid using color hex codes directly or using random design system variables for color.

The colors variables are defined in `styles/mcrColors.scss`. By reusing color variables from one place, we make future refactors easy and reduce styles tech debt. For example, if we needed to add a dark mode in the application or do a re-design, we start from one place. Note that we have colors in the application coming from both USWDS and CMDS currently in the application. These patterns are easier to see when our colors are defined in one place.

## Containers

Containers are the presentational elements that position content on the page. Look at designs and note the background color,the overall width of the content, and any obvious gutters between or margin/padding around content.

We currently have ~three types of containers in the application. See our `custom.scss` for scss variables and mixins related to containers. Whenever you create a new page, one of your first considerations should be the container, making sure it is using `flex`, and determining which type of container you need. Make sure your container stretches the height and width you expect by expanding and shrinking the viewport and seeing how content inside behaves. There should not be gaps, for example, between the bottom of the page container and the footer. Also note the background color of the container and the margin. If areas have a lot of gray space, that is likely the base color of our main HTML document body. If the background looks white, that's probably `$mcr-foundation-white`.

## Text

Make sure heading levels (`h2`, `h3` etc) are properly used for text content. There are clear [guidelines](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements) around heading levels to follow. For example, page titles should always have some kind of heading, use only one h1 per page, headings should be nested by level, etc. If the heading is not styled the way you expect by default, CSS can be used to override things like font weight, size, etc. If you are noticing something with heading that need to be overridden across the app, this likely calls for global styles.

Note also that there is a specific color to be used for links (`$$mcr-foundation-link`) and hint text (`$mcr-foundation-hint`).

## Technologies related to styles in MC-Review

### Sass / SCSS stylesheets

[Sass](https://sass-lang.com/documentation/file.SASS_REFERENCE.html) is the way we write styles in the project. It is an extension of CSS. All files that `.scss` extension use Sass. Some basic concepts of Sass that are good to understand/review that are listed below.

- How to [load SCSS](https://sass-lang.com/documentation/at-rules/use#loading-members)
- How to [use nested selectors](https://sass-lang.com/documentation/style-rules#nesting)
- How to [structure a Sass stylesheet](https://sass-lang.com/documentation/syntax/structure)

### United States Web Design System

See the ADR on [`Use USWDS as the design system`](../architectural-decision-records/014-use-uswds-design-system.md).

What this means for styling the application is that you will see some USWDS language and patterns. This can also be used a common language between design and engineering. For example, the names of our components follow [USWDS names for components](https://designsystem.digital.gov/components/overview/). And when you look at our application in the dev tool, every class with `usa-*` in front of it is coming from USWDS. USWDS has [design tokens](https://designsystem.digital.gov/design-tokens/) and mixins we can rely on as well.

## Style patterns in MC-Review

### Styles should be as narrowly scoped as possible

This is 101 but easy to forget. Please read [this section](https://github.com/trussworks/Engineering-Playbook/blob/main/docs/web/frontend/developing-ui.md#style-with-css-modules) of the Truss eng playbook for a good summary.

TL;DR Less is more with styles. In our application, the preferred way to style React code is via locally scoped [CSS module](https://github.com/css-modules/css-modules), written as a `<component>.module.scss` file. This is stored alongside React component files. We also use [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference#selectors) to further target styles. Tightly scoped styles and CSS [specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) ensure separation of concerns.

Example of scoped styles in use:
- Shared styles live close to the workflow they apply to. For example, directories in `/pages` folder may share a `*.module.scss` stylesheet if needed for templates in that area of the application. CSS selectors should be used to further scope down styles.
- Re-useable atomic components (see `/components` folder) have self contained styles. There is a named `*.module.scss` file for each component.


### Global styles live in `styles/custom.scss`

Global styles should be uncommmon. They generally change only during a styles refactor across the application.

The `styles/custom.scss` is also the main file imported into components to reference global SCSS variables. This file `@forward`s the MCR color palette and USWDS sass variables to the rest of the application.

### Import styles with `@use`
- Prefer namespaced scss imports with [`@use`](https://sass-lang.com/documentation/at-rules/use/) over [`@import`](https://sass-lang.com/documentation/at-rules/import/) for scss to reduce loading styles multiple times. Sass team is deprecating [`@import`](https://github.com/sass/sass/blob/main/accepted/module-system.md#timeline).

In `*.module.scss` files this looks like (at top of file):
```
@use '../../styles/custom.scss' as custom;
@use '../../styles/uswdsImports.scss' as uswds;
```

and then referring to varibles and mixins inline with the [namespace](https://sass-lang.com/documentation/at-rules/use/#choosing-a-namespace) - such as `custom.$mcr-foundation-white` or `@include uswds.uswds-at-media(tablet)`

### Watch out for global style overrides that can impact UI across the application.

Always nest your scss styles code in a css class to benefit from [the advantages of CSS/SCSS modules](https://css-tricks.com/css-modules-part-1-need/). Eencapsulates styles in classes throughout `[component].module.scss`. Do not use html and `usa-*` class overrides at the top level of the module files. This will override global styles across the application.

If you must, make global overrides changes in (`overrides.scss`). Here are concrete examples where global style overrides are needed:

1. We are dealing with container elements that repeat throughout the application outside of a specific component (divs, cards, display tables)
2. We need override a behavior in USWDS that we can't control via settings throughout the application
3. We want to hide an element from the screen using our global screenreader only class


### Compose together styles in React component files using the `classnames` package

- Sometimes React components have complex logic related to styles. To assist with this and follow patterns from `@trussworks/react-uswds` we use [`classnames`](https://github.com/JedWatson/classnames) package to compose together styles from different places. In the future if desired, we could make a similar utility in code and remove this dependency.

Example of complex conditional styles from ActionButton:

```react
const classes = classnames(
{
'usa-button--outline-disabled': isDisabled && isOutline,
'usa-button--disabled': isDisabled,
'usa-button--active': isLoading && !isSuccess,
[styles.successButton]: isSuccess && !isDisabled,
[styles.disabledCursor]: isDisabled || isLoading,
},
className
)
```

In this example, ActionButton has different states (loading, disabled,success) that can be combined and there are is secondary visual variant (outline) to consider.

The `'usa-button---'` classes are string literals for the class coming directly from USWDS. We want to use these in some cases. Then there are variables like `styles.disabledCursor` which come from the component module SCSS file. Finally, there is the `className` is coming from React props passed into the component from the parent. Ut is a best practice is to apply any consumer defined `className` styles last in re-useable components.

The logic can be understood as

1. USWDS styles are conditionally applied
2. component specific styles from the module `styles` object are conditionally applied
3. `className` prop is applied to all instances of the component (also overriding anything applied earlier if styles reference to the same CSS attribute).
14 changes: 14 additions & 0 deletions services/app-web/.stylelintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't been able to get this to work. I enabled vscode-stylelint to test this, are you using something different?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was running stylelint in app-web manually npx styelint **/*.scss- let me surface that as a yarn command also and document. It should be hooked into lint-staged as well (precommit script) I'll double check that.

"extends": "stylelint-config-recommended-scss",
"rules": {
"color-no-hex": true
},
"overrides": [
{
"files": ["src/styles/**"],
"rules": {
"color-no-hex": null
}
}
]
}
7 changes: 2 additions & 5 deletions services/app-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,13 @@ Open [http://localhost:6000](http://localhost:6000) to view in the browser.
- **`src/index.tsx`** main React application entrypoint - configures authentication, api, s3. Further configuration can be found in `src/pages/App`/
- `src/pages` folder is used for components and logic specific to a page (content that is inside `<main>`).
- `src/components` folder is used abstracted, reuseable components. These are components without significant routing or page-specific logic. Often these components work well in storybook.
- Follow guidance in the engineering playbook around [implementing UI](https://github.com/trussworks/Engineering-Playbook/blob/main/web/frontend/developing-ui.md)
- Use nested folders named after the component. Tests are stored alongside components.
- List file imports in this order: React imports, external imports, assets/styles imports, local imports. [needs automation]

### SCSS files

- Use modular styles. This means creating`<component>.module.scss` or `<component>.module.css` files in your component folders.
- Learn about [Sass](https://sass-lang.com/documentation/file.SASS_REFERENCE.html) and [CSS modules](https://github.com/css-modules/css-modules) as well [uswds scss classes, tokens, and mixins](https://designsystem.digital.gov/design-tokens/).
- Syntax: Sass styles should be written in camelCase. Import styles from a component's stylesheet using something like `import styles from 'InvoicePanel.module.scss'`. Access the styles with dot notation `styles.myclassname`. If fewer than 50% of the styles are used from a stylesheet, import only the styles used (ex. `import { myclassname } from 'MyComponent.module.scss'`).
- To reference sass variables, bring in uswds scss or project/cms scss as `@import 'styles/uswdsImports.scss';` and `@import 'styles/custom.scss'` accordingly.
- Use modular styles. This means creating`<component>.module.scss` or `<component>.module.css` files in your component folders.
- Read [how to styles](/docs/technical-design/howto-style-css-scss.md) documentation.

### `/gen`

Expand Down
11 changes: 9 additions & 2 deletions services/app-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"eject": "react-scripts eject",
"clean": "rm -rf node_modules && yarn cache clean",
"format": "prettier --write src/**/*.ts{,x}",
"lint": "tsc --noEmit && eslint src/ --max-warnings=0",
"lint": "tsc --noEmit && eslint src/ --max-warnings=0 && yarn lint:style",
"lint:style": "npx stylelint '**/*.scss'",
"test": "TZ=UTC react-scripts test",
"test:once": "TZ=UTC react-scripts test --watchAll=false --runInBand --ci",
"test:update": "react-scripts test -u",
Expand All @@ -22,9 +23,13 @@
"prettier": "npx prettier -u --write \"**/*.+(ts|tsx|json)\""
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"*.{js,jsx, ts,tsx}": [
"eslint --fix --max-warnings=0",
"prettier --write"
],
"**/*.scss": [
"yarn lint:style",
"prettier --write"
]
},
"jest": {
Expand Down Expand Up @@ -161,6 +166,8 @@
"serverless-cloudfront-invalidate": "^1.11.0",
"serverless-s3-sync": "^3.0.0",
"serverless-stack-termination-protection": "^2.0.2",
"stylelint": "^16.1.0",
"stylelint-config-recommended-scss": "^14.0.0",
"webpack": "^5.72.0",
"yarn": "^1.22.19"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@import '../../styles/uswdsImports.scss';
@import '../../styles/custom.scss';
@use '../../styles/custom.scss' as custom;
@use '../../styles/uswdsImports.scss' as uswds;

// preferred way to show a button that is disabled. avoid cursor: none
.disabledCursor {
Expand All @@ -16,11 +16,11 @@
}

.successButton {
background: $theme-color-success;
background: custom.$mcr-success-base;
&:hover {
background-color: #2a7a3b !important;
background-color: custom.$mcr-success-hover !important;
}
&:active {
background-color: #2a7a3b !important;
background-color: custom.$mcr-success-hover !important;
}
}
4 changes: 2 additions & 2 deletions services/app-web/src/components/Banner/Banner.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@import '../../styles/uswdsImports.scss';
@import '../../styles/custom.scss';
@use '../../styles/custom.scss' as custom;
@use '../../styles/uswdsImports.scss' as uswds;

.bannerBodyText {
p {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import '../../styles/uswdsImports.scss';
@use '../../styles/custom.scss' as custom;
@use '../../styles/uswdsImports.scss' as uswds;

.crumbContainer {
margin-top: 1em;
Expand All @@ -8,7 +9,7 @@
[class^='usa-breadcrumb'] {
li:last-child a {
text-decoration: none;
color: $theme-color-base-ink;
color: custom.$mcr-foundation-ink
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
@import '../../styles/uswdsImports.scss';
@import '../../styles/custom.scss';
@use '../../styles/custom.scss' as custom;
@use '../../styles/uswdsImports.scss' as uswds;

.summarySection {
background: $cms-color-white;
line-height: units(3);
@include u-radius('md');
background: custom.$mcr-foundation-white;
line-height: uswds.units(3);
@include uswds.u-radius('md');

h2 {
margin: 0;
@include u-text('normal');
@include uswds.u-text('normal');
}
&:first-of-type {
h2 {
@include u-text('bold');
@include uswds.u-text('bold');
font-size: size('body', 'lg');
}
}
Expand Down
56 changes: 56 additions & 0 deletions services/app-web/src/components/Colors/Colors.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// stylelint-disable selector-pseudo-class-no-unknown
@use '../../styles/custom.scss' as custom;

:export {
mcr: {
primary: {
lighter: custom.$mcr-primary-lighter;
light: custom.$mcr-primary-light;
base: custom.$mcr-primary-base;
dark: custom.$mcr-primary-dark;
darkest: custom.$mcr-primary-darkest;
}
cmsblue: {
lightest: custom.$mcr-cmsblue-lightest;
base: custom.$mcr-cmsblue-base;
dark: custom.$mcr-cmsblue-dark;
darkest: custom.$mcr-cmsblue-darkest;
}
cyan: {
light: custom.$mcr-cyan-light;
base: custom.$mcr-cyan-base;
dark: custom.$mcr-cyan-dark;
}
gold: {
base: custom.$mcr-gold-base;
dark: custom.$mcr-gold-dark;
darker: custom.$mcr-gold-darker;
}
gray: {
dark: custom.$mcr-gray-dark;
base : custom.$mcr-gray-base;
lighter: custom.$mcr-gray-lighter;
lightest: custom.$mcr-gray-lightest;
}
foundation: {
white: custom.$mcr-foundation-white;
ink : custom.$mcr-foundation-ink;
hint: custom.$mcr-foundation-hint;
link: custom.$mcr-foundation-link;
focus: custom.$mcr-foundation-focus;
visited: custom.$mcr-foundation-visited;

}
success: {
base: custom.$mcr-success-base;
hover: custom.$mcr-success-hover;
dark: custom.$mcr-success-dark;
}
error: {
light: custom.$mcr-error-light;
base: custom.$mcr-error-base;
dark: custom.$mcr-error-dark;
}
}

}
Loading
Loading