From 95590932aae989c7a3dbcb04c5dea581bf2fe970 Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Thu, 5 Sep 2024 07:16:24 -0700
Subject: [PATCH 01/15] wip
---
contributor-docs/authoring-css.md | 46 +++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 contributor-docs/authoring-css.md
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
new file mode 100644
index 00000000000..a1c5c920068
--- /dev/null
+++ b/contributor-docs/authoring-css.md
@@ -0,0 +1,46 @@
+# Authoring CSS
+
+Primer React uses CSS Modules(link) for styling. CSS Modules allows us to write component scoped CSS while still authoring in a traditional `.css` file. This guide covers best practices for writing CSS in Primer React.
+
+## Getting started
+
+## Code styles
+
+
+
+- classname style
+- pseudo elements
+- nesting
+- variants data attributes
+- use where
+- classname at the top level of a component
+- no fallbacks
+- baseline 2022
+- supports
+- one file per component
+- linting
+
+Prefer class names that are easy to import in JavaScript
+Tip: avoid dashes, use camelCase or PascalCase
+Prefer pseudo-class over a custom class name
+Tip: use .ComponentName:disabled over .ComponentName--disabled
+Prefer data attributes over modifier classes
+Tip: data-variant=”primary” over .ComponentName--primary
+Prefer selectors with the least specificity needed
+Tip: avoid deep nesting as it creates higher specificity selectors
+Tip: use the :where selector to have a specificity of 0, for example :where([data-variant=”primary’])
+Import CSS modules after JS imports to avoid certain CSS ordering issues
+When allowing className to be passed as a prop, prefer supporting a className only on the top-level element that is rendered instead of supporting multiple className’s
+If targeting a in a component is needed, prefer using a stable class name
+Note: the specificity of styles applied to this part of the component is not a part of the public API
+Another alternative is to allow customization through CSS Custom Properties that can be set with a custom className
+[Primer] avoid setting fallback values for design tokens from Primitives (these are added automatically)
+Ideally use CSS features no newer than Baseline 2022
+When using CSS features from Baseline 2023 or newer, provide an appropriate fallback for when the feature is unavailable
+Tip: use @supports to target when a specific piece of functionality is not available
+[Primer] Write selectors that target components that the current component owns
+For example, don’t put all the styles for several components across different files in one single CSS Module file
+Tip: when needing to style or target child components, consider using CSS Custom Properties as a bridge between parent and child (reference)
+Prefer one CSS Module file per component
+Caveat: it is okay to have multiple components in a file and it is okay to have one CSS Module file for this scenario
+Over time, breakout as-needed
From 19cbbab6b43a62d1697abdf1672975880e4bdf5d Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Wed, 9 Oct 2024 09:18:14 -0700
Subject: [PATCH 02/15] first pass
---
contributor-docs/authoring-css.md | 186 ++++++++++++++++++++++++++----
1 file changed, 162 insertions(+), 24 deletions(-)
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
index a1c5c920068..eed83149fc1 100644
--- a/contributor-docs/authoring-css.md
+++ b/contributor-docs/authoring-css.md
@@ -1,46 +1,184 @@
# Authoring CSS
-Primer React uses CSS Modules(link) for styling. CSS Modules allows us to write component scoped CSS while still authoring in a traditional `.css` file. This guide covers best practices for writing CSS in Primer React.
+Primer React uses CSS Modules(link) for styling. CSS Modules allow us to write component scoped CSS while still authoring in a traditional `.css` file. This guide covers best practices for writing CSS in Primer React.
## Getting started
+### File setup
+
+Create a new `.css` file in the same directory as the component you are working on. Name the file the same as the component, and add the extension `.module.css`.
+
+Example: `Button.modules.css`
+
+### Importing CSS
+
+Import the new CSS file into the component TSX file.
+
+```tsx
+import classes from './Button.module.css'
+```
+
+### Reference CSS classes
+
+Reference CSS classes in the component TSX file using the `classes` object.
+
+```css
+/* Banner.module.css */
+
+.Banner {
+ background-color: var(--banner-bgColor);
+}
+```
+
+```tsx
+// Banner.tsx
+
+import classes from './Button.module.css'
+
+
Banner>
+```
+
## Code styles
+### CSS class names
+
+When component classnames are compiled, they receive a prefix of the component name `prc-{component}-` and a suffix of a random hash.
+
+```css
+/* Before compilation */
+.Container {
+ display: inline-block;
+}
+
+/* After compilation */
+.prc-Button-Container-cBBI {
+ display: inline-block;
+}
+```
+
+Since classes are prefixed and hashed, the class names themselves can be named generically to represent their intention.
+
+#### PascalCase
+
+Use PascalCase for class names. Additional characters like `-` dashes or `_` underscores must be escaped with a `\` backslash in TSX for the class name to be recognized, which can be cumbersome.
+
+```css
+
+/* Do */
+.ButtonContent {
+ display: inline-block;
+}
+
+/* Don't */
+.button-content {
+ display: inline-block;
+}
+```
+
+#### Pseudo elements
+
+Prefer using pseudo elements over classnames for state.
+
+```css
+/* Do */
+.Button:disabled {
+ opacity: 0.5;
+}
+
+/* Don't */
+.ButtonDisabled {
+ opacity: 0.5;
+}
+```
+
+### Component prop variants as data-attributes
+
+When a component has a variant, prefer using a data-attribute over a modifier class.
+
+Some common variants include:
+
+- data-size
+- data-variant
+- data-loading
+
+```css
+/* Do */
+.Button {
+ &:where([data-size='small']) {
+ height: var(--control-small-size);
+ }
+}
+
+/* Don't */
+.Button {
+ &--small {
+ height: var(--control-small-size);
+ }
+}
+```
+
+### Specificity and nesting
+
+Whenever possible, avoid deep nesting as it creates higher specificity selectors. Rely on stylelint to guide how many levels of nesting are acceptable.
+
+#### Using `:where` to reduce specificity
+
+The `:where` selector has a specificity of 0, which can be useful for allowing custom overrides. Use the `:where` selector for component options that utilize data-attributes like `&:where([data-size='small'])`.
+
+### CSS variables
+
+#### Primer primitives
+
+Use CSS variables from `@primer/primitives` for size, typography, and color values. Certain components also have their own pattern level CSS variables from `@primer/primitives` that should be used.
+
+#### Component CSS variables
+
+CSS variables may also be used contextually to set component variants. These CSS variables are defined within the component CSS file.
+
+```css
+.Banner {
+ background-color: var(--banner-bgColor);
+
+ &:where([data-variant='critical']) {
+ --banner-bgColor: var(--bgColor-danger-muted);
+ }
+}
+```
+
+#### Fallbacks
+
+Avoid adding fallback values to CSS variables from `@primer/primitives`. These are added automatically and will be compiled to CSS variables with a fallback value.
-- classname style
-- pseudo elements
-- nesting
-- variants data attributes
-- use where
- classname at the top level of a component
-- no fallbacks
+
- baseline 2022
- supports
- one file per component
- linting
-Prefer class names that are easy to import in JavaScript
-Tip: avoid dashes, use camelCase or PascalCase
-Prefer pseudo-class over a custom class name
-Tip: use .ComponentName:disabled over .ComponentName--disabled
-Prefer data attributes over modifier classes
-Tip: data-variant=”primary” over .ComponentName--primary
-Prefer selectors with the least specificity needed
-Tip: avoid deep nesting as it creates higher specificity selectors
-Tip: use the :where selector to have a specificity of 0, for example :where([data-variant=”primary’])
+### Support
+
+Prefer CSS features no newer than Baseline 2022. When using CSS features from Baseline 2023 or newer, provide an appropriate fallback for when the feature is unavailable.
+
+Use `@supports` to target when a specific piece of functionality is not available
+
+```css
+@supports (container-type: inline-size) {
+ container: banner / inline-size;
+}
+```
+
+### Extra notes below
+
Import CSS modules after JS imports to avoid certain CSS ordering issues
When allowing className to be passed as a prop, prefer supporting a className only on the top-level element that is rendered instead of supporting multiple className’s
-If targeting a in a component is needed, prefer using a stable class name
-Note: the specificity of styles applied to this part of the component is not a part of the public API
-Another alternative is to allow customization through CSS Custom Properties that can be set with a custom className
-[Primer] avoid setting fallback values for design tokens from Primitives (these are added automatically)
-Ideally use CSS features no newer than Baseline 2022
-When using CSS features from Baseline 2023 or newer, provide an appropriate fallback for when the feature is unavailable
-Tip: use @supports to target when a specific piece of functionality is not available
+
+
+
[Primer] Write selectors that target components that the current component owns
For example, don’t put all the styles for several components across different files in one single CSS Module file
-Tip: when needing to style or target child components, consider using CSS Custom Properties as a bridge between parent and child (reference)
+Tip: when needing to style or target child components, consider using CSS
Prefer one CSS Module file per component
Caveat: it is okay to have multiple components in a file and it is okay to have one CSS Module file for this scenario
Over time, breakout as-needed
From e9ec77cd4e2b47e1e920e146f90a526c655bf5db Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Wed, 9 Oct 2024 11:06:25 -0700
Subject: [PATCH 03/15] messy notes from pair
---
contributor-docs/authoring-css.md | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
index eed83149fc1..861a60b88c2 100644
--- a/contributor-docs/authoring-css.md
+++ b/contributor-docs/authoring-css.md
@@ -91,6 +91,10 @@ Prefer using pseudo elements over classnames for state.
}
```
+### `clsx` and className
+
+only at top level (once)
+
### Component prop variants as data-attributes
When a component has a variant, prefer using a data-attribute over a modifier class.
@@ -103,20 +107,21 @@ Some common variants include:
```css
/* Do */
-.Button {
- &:where([data-size='small']) {
- height: var(--control-small-size);
- }
+.Button:where([data-size='small']) {
+ height: var(--control-small-size);
}
/* Don't */
-.Button {
- &--small {
- height: var(--control-small-size);
- }
+.ButtonSmall {
+ height: var(--control-small-size);
}
```
+TODO add responsive data-variants
+
+data-variant as string
+data-variant as boolean
+
### Specificity and nesting
Whenever possible, avoid deep nesting as it creates higher specificity selectors. Rely on stylelint to guide how many levels of nesting are acceptable.
@@ -182,3 +187,6 @@ Tip: when needing to style or target child components, consider using CSS
Prefer one CSS Module file per component
Caveat: it is okay to have multiple components in a file and it is okay to have one CSS Module file for this scenario
Over time, breakout as-needed
+
+side-effect properties
+stylelint rule to check proper flex usage
From f99ca1fc3bb782d1f0e88433bd7d1d4e7ff01892 Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Thu, 17 Oct 2024 09:52:21 -0700
Subject: [PATCH 04/15] second pass
---
contributor-docs/authoring-css.md | 130 +++++++++++++++++++++++-------
1 file changed, 102 insertions(+), 28 deletions(-)
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
index 861a60b88c2..0c0e4915a6d 100644
--- a/contributor-docs/authoring-css.md
+++ b/contributor-docs/authoring-css.md
@@ -40,7 +40,7 @@ import classes from './Button.module.css'
## Code styles
-### CSS class names
+### CSS classnames
When component classnames are compiled, they receive a prefix of the component name `prc-{component}-` and a suffix of a random hash.
@@ -56,11 +56,11 @@ When component classnames are compiled, they receive a prefix of the component n
}
```
-Since classes are prefixed and hashed, the class names themselves can be named generically to represent their intention.
+Since classes are prefixed and hashed, the classnames themselves can be named generically to represent their intention.
#### PascalCase
-Use PascalCase for class names. Additional characters like `-` dashes or `_` underscores must be escaped with a `\` backslash in TSX for the class name to be recognized, which can be cumbersome.
+Use PascalCase for classnames. Additional characters like `-` dashes or `_` underscores must be escaped with a `\` backslash in TSX for the class name to be recognized, which can be cumbersome.
```css
@@ -93,11 +93,45 @@ Prefer using pseudo elements over classnames for state.
### `clsx` and className
-only at top level (once)
+Multiple classnames can be referenced on a single node using the `clsx` utility. This is also useful for providing a `className` prop alongside the default class name.
+
+The `className` prop should only be offered on the top-level element of a component. Avoid offering multiple layers of `className` props to child elements. Consider offering a CSS variable for properties that a consumer may need to customize at the lower levels.
+
+Ensure that other `...props` are spread before the `className` prop to avoid being overridden.
+
+```tsx
+import clsx from 'clsx'
+
+export function Button({className, ...props}) {
+ return
+}
+```
+
+```tsx
+// don't offer multiple classNames
+export function Button({className, labelClassName}) {
+ return
+
+}
+```
+
+### Responsive design
+
+We utilize PostCSS to allow for CSS variables to be used within media queries. The list of available media queries can be found in the [@primer/primitives viewport documentation](https://primer.style/foundations/primitives/size#viewport).
+
+To use a viewport variable, write the `@media` rule as normal and place the variable in between the parentheses.
+
+```css
+@media screen and (--viewportRange-regular) {
+ /* styles */
+}
+```
### Component prop variants as data-attributes
-When a component has a variant, prefer using a data-attribute over a modifier class.
+When a component has a variant, prefer using a data attribute over a modifier class.
Some common variants include:
@@ -117,10 +151,70 @@ Some common variants include:
}
```
-TODO add responsive data-variants
+Data attributes can be used as a boolean to represent a true or false state, or as a string to represent a specific value.
+
+```css
+
+/* boolean */
+
+.Button:where([data-loading]) {
+ cursor: not-allowed;
+}
+
+/* string */
+
+.Button:where([data-size='small']) {
+ height: var(--control-small-size);
+}
+
+```
+
+#### Responsive data attributes
+
+It is common to offer responsive props that allow the consumer to set styling based on the viewport size. This functionality can be extended via data attributes.
+
+```tsx
+import type {ResponsiveValue} from '../hooks/useResponsiveValue'
+import {getResponsiveAttributes} from '../internal/utils/getResponsiveAttributes'
+
+// types
+type PaddingScale = 'none' | 'condensed' | 'normal' | 'spacious'
+type Padding = PaddingScale | ResponsiveValue
-data-variant as string
-data-variant as boolean
+// prop
+type StackProps = {
+ padding?: Padding
+}
+
+// component
+export function Stack({padding = 'normal'}: StackProps) {
+ return
+}
+
+// usage
+
+```
+
+By default, we may offer a `padding` prop. The data attribute for `padding` might look like `data-padding="normal"`. To make the `padding` prop responsive, utilize the [ResponsiveValue](https://github.com/primer/packages/react/src/hooks/useResponsiveValue.ts) hook alongside the [getResponsiveAttributes](https://github.com/primer/react/src/internal/utils/getResponsiveAttributes.ts) utility.
+
+```tsx
+// apply the responsive data-attributes using getResponsiveAttributes
+export function Stack({padding = 'normal'}: StackProps) {
+ return
+}
+```
+
+By using `getResponsiveAttributes`, we can reference data attributes in the CSS file based on the prop type offerings.
+
+```css
+/* Stack.module.css */
+.Stack {
+ &:where([data-padding='none']),
+ &:where([data-padding-narrow='none']) {
+ padding: 0;
+ }
+}
+```
### Specificity and nesting
@@ -154,14 +248,6 @@ CSS variables may also be used contextually to set component variants. These CSS
Avoid adding fallback values to CSS variables from `@primer/primitives`. These are added automatically and will be compiled to CSS variables with a fallback value.
-
-- classname at the top level of a component
-
-- baseline 2022
-- supports
-- one file per component
-- linting
-
### Support
Prefer CSS features no newer than Baseline 2022. When using CSS features from Baseline 2023 or newer, provide an appropriate fallback for when the feature is unavailable.
@@ -178,15 +264,3 @@ Use `@supports` to target when a specific piece of functionality is not availabl
Import CSS modules after JS imports to avoid certain CSS ordering issues
When allowing className to be passed as a prop, prefer supporting a className only on the top-level element that is rendered instead of supporting multiple className’s
-
-
-
-[Primer] Write selectors that target components that the current component owns
-For example, don’t put all the styles for several components across different files in one single CSS Module file
-Tip: when needing to style or target child components, consider using CSS
-Prefer one CSS Module file per component
-Caveat: it is okay to have multiple components in a file and it is okay to have one CSS Module file for this scenario
-Over time, breakout as-needed
-
-side-effect properties
-stylelint rule to check proper flex usage
From 972c0f5a0e9d6218987355af3afc5870ef808c02 Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Thu, 17 Oct 2024 09:53:06 -0700
Subject: [PATCH 05/15] add link
---
contributor-docs/authoring-css.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
index 0c0e4915a6d..22cf3b6efc5 100644
--- a/contributor-docs/authoring-css.md
+++ b/contributor-docs/authoring-css.md
@@ -1,6 +1,6 @@
# Authoring CSS
-Primer React uses CSS Modules(link) for styling. CSS Modules allow us to write component scoped CSS while still authoring in a traditional `.css` file. This guide covers best practices for writing CSS in Primer React.
+Primer React uses [CSS Modules](https://github.com/css-modules/css-modules) for styling. CSS Modules allow us to write component scoped CSS while still authoring in a traditional `.css` file. This guide covers best practices for writing CSS in Primer React.
## Getting started
From 62185b6f0b2abb50128f8994190c61552e631f37 Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Thu, 17 Oct 2024 09:56:15 -0700
Subject: [PATCH 06/15] remove notes
---
contributor-docs/authoring-css.md | 5 -----
1 file changed, 5 deletions(-)
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
index 22cf3b6efc5..56e17d8cee5 100644
--- a/contributor-docs/authoring-css.md
+++ b/contributor-docs/authoring-css.md
@@ -259,8 +259,3 @@ Use `@supports` to target when a specific piece of functionality is not availabl
container: banner / inline-size;
}
```
-
-### Extra notes below
-
-Import CSS modules after JS imports to avoid certain CSS ordering issues
-When allowing className to be passed as a prop, prefer supporting a className only on the top-level element that is rendered instead of supporting multiple className’s
From eaf9744b762a9804000994c7430e4a7135b73768 Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Thu, 17 Oct 2024 11:58:14 -0700
Subject: [PATCH 07/15] Apply suggestions from code review
Co-authored-by: Josh Black
---
contributor-docs/authoring-css.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
index 56e17d8cee5..f6abb505ab6 100644
--- a/contributor-docs/authoring-css.md
+++ b/contributor-docs/authoring-css.md
@@ -42,7 +42,7 @@ import classes from './Button.module.css'
### CSS classnames
-When component classnames are compiled, they receive a prefix of the component name `prc-{component}-` and a suffix of a random hash.
+When component classnames are compiled, they receive a prefix of the component name `prc-{folder}-{local}-` and a suffix of a random hash.
```css
/* Before compilation */
@@ -56,7 +56,7 @@ When component classnames are compiled, they receive a prefix of the component n
}
```
-Since classes are prefixed and hashed, the classnames themselves can be named generically to represent their intention.
+Since classes are prefixed and hashed, the class names themselves can be named generically to represent their intention.
#### PascalCase
@@ -77,7 +77,7 @@ Use PascalCase for classnames. Additional characters like `-` dashes or `_` unde
#### Pseudo elements
-Prefer using pseudo elements over classnames for state.
+Prefer using pseudo classes over classnames for state.
```css
/* Do */
From ef09b6be66ecc83802ca86ac247ca4bc949b0e5b Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Thu, 17 Oct 2024 11:59:05 -0700
Subject: [PATCH 08/15] format
---
contributor-docs/authoring-css.md | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
index f6abb505ab6..7f47d7d2fe3 100644
--- a/contributor-docs/authoring-css.md
+++ b/contributor-docs/authoring-css.md
@@ -110,10 +110,11 @@ export function Button({className, ...props}) {
```tsx
// don't offer multiple classNames
export function Button({className, labelClassName}) {
- return
-
+ return (
+
+ )
}
```
From 082b56f40824e3d65e1f9a16e4dce11299169146 Mon Sep 17 00:00:00 2001
From: Katie Langerman <18661030+langermank@users.noreply.github.com>
Date: Thu, 17 Oct 2024 12:55:27 -0700
Subject: [PATCH 09/15] lint
---
contributor-docs/authoring-css.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/contributor-docs/authoring-css.md b/contributor-docs/authoring-css.md
index 7f47d7d2fe3..555a7885744 100644
--- a/contributor-docs/authoring-css.md
+++ b/contributor-docs/authoring-css.md
@@ -35,7 +35,7 @@ Reference CSS classes in the component TSX file using the `classes` object.
import classes from './Button.module.css'
-
Banner>
+
Banner
```
## Code styles
@@ -100,10 +100,10 @@ The `className` prop should only be offered on the top-level element of a compon
Ensure that other `...props` are spread before the `className` prop to avoid being overridden.
```tsx
-import clsx from 'clsx'
+import {clsx} from 'clsx'
export function Button({className, ...props}) {
- return
+ return
}
```
@@ -111,7 +111,7 @@ export function Button({className, ...props}) {
// don't offer multiple classNames
export function Button({className, labelClassName}) {
return (
-