Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
19 changes: 19 additions & 0 deletions .changeset/free-planets-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@primer/react-brand': minor
---

Added ESM library support.

A new `@primer/react-brand/esm` entry point is now available for modern bundlers (E.g. Vite, Next.js, etc).

Components imported from this path automatically include the minimum CSS needed. Separate global stylesheet imports are no longer required.

```js
import {Hero, ThemeProvider} from '@primer/react-brand/esm'
```

ESM features are opt-in, and the previous UMD bundle (`@primer/react-brand`) and global CSS (`@primer/react-brand/lib/css/main.css`) continue to work as before.

We recommend switching to ESM as soon as possible, as it will eventually become the default in future.

🔗 [Get started with ESM](https://primer.style/brand/introduction/getting-started/esm)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
🔗 [Get started with ESM](https://primer.style/brand/introduction/getting-started/esm)
🔗 [Get started with ESM](https://primer.style/brand/getting-started/esm)

65 changes: 65 additions & 0 deletions .github/workflows/bundle_size.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Check bundle size

on:
pull_request:

jobs:
size:
if: ${{ github.repository == 'primer/brand' }}
name: Report bundle size
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Create main branch worktree
run: git worktree add /tmp/main ${{ github.event.pull_request.base.sha }}

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 24

- name: Caching dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

- name: Install and build (PR)
run: |
npm ci
npm run build:lib

- name: Measure bundle size (PR)
run: npx size-limit --json > /tmp/pr-sizes.json

- name: Install and build (main branch)
working-directory: /tmp/main
run: |
npm ci
cp ${{ github.workspace }}/.size-limit.js .size-limit.js
npm run build:lib || true

- name: Measure bundle size (main branch)
working-directory: /tmp/main
run: npx size-limit --json > /tmp/main-sizes.json 2>/dev/null || echo "[]" > /tmp/main-sizes.json

- name: Generate size report
id: size-report
run: |
REPORT=$(node packages/repo-configs/scripts/bundle-size-report.js /tmp/pr-sizes.json /tmp/main-sizes.json)
echo "comment-body<<EOF" >> "$GITHUB_OUTPUT"
echo "$REPORT" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"

- name: Add or update bundle size comment
uses: phulsechinmay/rewritable-pr-comment@v0.3.0
with:
message: ${{ steps.size-report.outputs.comment-body }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COMMENT_IDENTIFIER: 'bundle-size-report'
69 changes: 32 additions & 37 deletions .github/workflows/integration_test_nextjs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Integration test
name: Integration test (Next.js)

on:
push:
Expand All @@ -8,15 +8,23 @@ on:
- cron: '0 0 * * *' # end of each day (UTC)
pull_request:

env:
NEXTJS_FOLDER: next-js-test

jobs:
test:
if: ${{ github.repository == 'primer/brand' }}
name: Next.js
name: Next.js (${{ matrix.bundle }})
runs-on:
labels: ubuntu-latest-16-cores
strategy:
fail-fast: false
matrix:
bundle: [umd, esm]
include:
- bundle: umd
nextjs_folder: next-js-test
- bundle: esm
nextjs_folder: next-js-esm-test
env:
NEXTJS_FOLDER: ${{ matrix.nextjs_folder }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
Expand All @@ -34,10 +42,6 @@ jobs:
restore-keys: |
${{ runner.os }}-node-

# Restore the previous NPM modules and Cypress binary archives.
# Any updated archives will be saved automatically after the entire
# workflow successfully finishes.
# See https://github.com/actions/cache
- name: Cache Cypress binary
uses: actions/cache@v4
with:
Expand All @@ -51,9 +55,7 @@ jobs:

- name: install dependencies and verify Cypress
env:
# make sure every Cypress install prints minimal information
CI: 1
# print Cypress and OS info
run: |
npx cypress install --force
npx cypress verify
Expand All @@ -71,7 +73,7 @@ jobs:
run: cd ./packages/react && npm pack

- name: Configuring Next.js
run: yes | npx create-next-app@latest ${{env.NEXTJS_FOLDER}} --typescript
run: yes | npx create-next-app@latest ${{ env.NEXTJS_FOLDER }} --typescript

- name: Retrieving package version
id: package-version
Expand All @@ -81,45 +83,38 @@ jobs:

- name: Installing local build
run: |
cd ${{env.NEXTJS_FOLDER}}
cp ../packages/react/primer-react-brand-${{ steps.package-version.outputs.current-version}}.tgz ./
npm install primer-react-brand-${{ steps.package-version.outputs.current-version}}.tgz
cd ${{ env.NEXTJS_FOLDER }}
cp ../packages/react/primer-react-brand-${{ steps.package-version.outputs.current-version }}.tgz ./
npm install primer-react-brand-${{ steps.package-version.outputs.current-version }}.tgz

- name: Prefer ESM imports
- name: Prepare barrel file for bundle type
run: |
mv ./packages/e2e/integration-tests/fixtures/index.esm.ts ./packages/e2e/integration-tests/fixtures/index.ts
rm ./packages/e2e/integration-tests/fixtures/index.cjs.ts
cp ./packages/e2e/integration-tests/fixtures/index.${{ matrix.bundle }}.ts ./packages/e2e/integration-tests/fixtures/index.ts
rm ./packages/e2e/integration-tests/fixtures/index.umd.ts ./packages/e2e/integration-tests/fixtures/index.esm.ts

- name: Copying required files
run: |
rm ./${{env.NEXTJS_FOLDER}}/app/page.tsx
cp ./packages/e2e/integration-tests/nextjs/page.tsx ./${{env.NEXTJS_FOLDER}}/app
rm ./${{ env.NEXTJS_FOLDER }}/app/page.tsx
cp ./packages/e2e/integration-tests/nextjs/page.tsx ./${{ env.NEXTJS_FOLDER }}/app
cp ./packages/e2e/cypress.config.js ./${{ env.NEXTJS_FOLDER }}
mkdir ${{ env.NEXTJS_FOLDER }}/integration-tests
cp -r ./packages/e2e/integration-tests/fixtures ./${{ env.NEXTJS_FOLDER }}/integration-tests
cp -r ./packages/e2e/integration-tests/tests ./${{ env.NEXTJS_FOLDER }}/integration-tests

- name: Copying required files
run: |
cp ./packages/e2e/integration-tests/nextjs/page.tsx ./${{env.NEXTJS_FOLDER}}/app
cp ./packages/e2e/cypress.config.js ./${{env.NEXTJS_FOLDER}}
mkdir ${{env.NEXTJS_FOLDER}}/integration-tests
cp -r ./packages/e2e/integration-tests/fixtures ./${{env.NEXTJS_FOLDER}}/integration-tests
cp -r ./packages/e2e/integration-tests/tests ./${{env.NEXTJS_FOLDER}}/integration-tests

- name: Excluded cypress tests in-place
# includes temp workaround for cypress bug. remove when fixed
# https://github.com/cypress-io/cypress/issues/27448
run: npx json -I -f ${{env.NEXTJS_FOLDER}}/tsconfig.json -e 'this.exclude=["node_modules", "**/*.cy.ts"]; this.compilerOptions.moduleResolution="node"'
- name: Exclude Cypress tests from tsconfig
run: npx json -I -f ${{ env.NEXTJS_FOLDER }}/tsconfig.json -e 'this.exclude=["node_modules", "**/*.cy.ts"]'

- name: Fix monorepo-related linting issues
run: |
npm run lint -- --fix
run: npm run lint -- --fix

# Needed to fix compilation errors and conflicts between TS and React type defs.
# All subsequent steps should not be dependent on the primer/brand repo contents.
- name: Clean workspace before testing
run: |
find . -maxdepth 1 -mindepth 1 -not -name ${{env.NEXTJS_FOLDER}} -exec rm -rf {} +
find . -maxdepth 1 -mindepth 1 -not -name ${{ env.NEXTJS_FOLDER }} -exec rm -rf {} +

- name: Testing compile-time build
run: cd ${{env.NEXTJS_FOLDER}} && npm run build
run: cd ${{ env.NEXTJS_FOLDER }} && npm run build

- name: Test runtime for errors and warnings
run: cd ${{env.NEXTJS_FOLDER}} && npx start-server-and-test 'start' 3000 'npx cypress run --spec "./integration-tests/tests/*" --config video=false'
run: cd ${{ env.NEXTJS_FOLDER }} && npx start-server-and-test 'start' 3000 'npx cypress run --spec "./integration-tests/tests/*" --config video=false'
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ node_modules

#output
lib/
esm/
storybook-static

# testing
Expand Down Expand Up @@ -35,4 +36,5 @@ yarn-error.log*
next-js-test/
cra-test/
astro-test/
remix-test/
remix-test/
esm-test/
32 changes: 32 additions & 0 deletions .size-limit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module.exports = [
{
name: 'UMD — full bundle (JS)',
path: 'packages/react/lib/index.js',
},
{
name: 'UMD — full bundle (CSS)',
path: 'packages/react/lib/css/main.css',
},
{
name: 'ESM — full bundle (JS + CSS)',
path: 'packages/react/esm/**/*.{js,css}',
},
{
name: 'ESM — tree-shaken simple (Button)',
path: 'packages/react/esm/index.esm.js',
import: '{ Button }',
modifyEsbuildConfig(config) {
config.external = ['react', 'react-dom']
return config
},
},
{
name: 'ESM — tree-shaken complex (ActionMenu)',
path: 'packages/react/esm/index.esm.js',
import: '{ ActionMenu }',
modifyEsbuildConfig(config) {
config.external = ['react', 'react-dom']
return config
},
},
]
56 changes: 56 additions & 0 deletions apps/next-docs/content/getting-started/esm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: Getting started
description: Primer Brand is open-sourced on GitHub and available on npm.
menu-position: 1
keywords: ['accessibility', 'introduction', 'simple']
show-tabs: true
tab-label: ESM
---

Using ESM, you will load only the React components and CSS you need.

We recommend using ESM if your project uses compatible bundlers or frameworks like Vite or Next.js.

### 1. Install Primer Brand

```shell
npm install @primer/react-brand
```

### 2. Import components from the ESM folder

```js
import {Hero, ThemeProvider} from '@primer/react-brand/esm'
```

Each component's styles are automatically included when you import the component.

<Note>
<p>
Do not combine this approach with the global <code>lib/css/main.css</code> import.
</p>

<p>Choose one approach or the other to avoid loading duplicate styles.</p>
</Note>

### 3. Import the font assets

The Mona Sans typeface still needs to be loaded separately:

```js
import '@primer/react-brand/fonts/fonts.css'
```

### 4. Use the `ThemeProvider`

```js
import {ThemeProvider} from '@primer/react-brand/esm'

function App() {
return (
<ThemeProvider>
<div>...</div>
</ThemeProvider>
)
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
title: Getting started
description: Primer Brand is open-sourced on GitHub and available on npm.
menu-position: 1
keywords: ['accessibility', 'introduction', 'simple']
keywords: ['introduction', 'simple', 'get started', 'tutorials']
show-tabs: true
tab-label: Default
---

Primer Brand is [open-sourced on GitHub](https://github.com/primer/brand) and [available on npm](https://www.npmjs.com/package/@primer/react-brand).
Expand All @@ -19,6 +21,11 @@ npm install @primer/react-brand

Primer Brand requires a global stylesheet to be loaded ahead-of-time.

<Note>
Refer to [this separate guide if you are using an ESM-compatible bundler](./esm.mdx). Component styles are included
with the React component imports.
</Note>

#### a. Using an application bundler (e.g. Webpack)

Import the `main` stylesheet at the earliest rendering opportunity:
Expand Down
1 change: 1 addition & 0 deletions apps/next-docs/content/introduction/animation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
title: Animation
storybook: '/brand/storybook/?path=/story/components-animations--playground'
description: Use animations to add visual interest and interactivity to a web page or application.
menu-position: 3
options:
disablePageAnimation: true
---
Expand Down
4 changes: 2 additions & 2 deletions apps/next-docs/content/introduction/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Introduction
title: Guides
description: Get started with Primer Brand
menu-position: 1
menu-position: 2
---
1 change: 1 addition & 0 deletions apps/next-docs/content/introduction/theming.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Theming
menu-position: 4
---

Primer Brand defines various global styling defaults using CSS variables, while also allowing app-level customization through its `ThemeProvider`.
Expand Down
2 changes: 1 addition & 1 deletion apps/next-docs/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
2 changes: 1 addition & 1 deletion apps/next-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
},
"dependencies": {
"@primer/doctocat-nextjs": "0.9.1",
"@primer/octicons-react": "19.21.2",
"@primer/octicons-react": "19.22.0",
"next": "16.1.6",
"react": "19.2.0",
"react-dom": "19.2.0"
Expand Down
Loading
Loading