Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3e98136
feat(tokens): add primitive colour palette and semantic token layer
talissoncosta Mar 10, 2026
8cc911c
feat(storybook): add Storybook 8 with colour palette and token stories
talissoncosta Mar 10, 2026
be8c991
ci: add Chromatic visual regression workflow for Storybook
talissoncosta Mar 10, 2026
a68af94
docs(storybook): add Introduction welcome page
talissoncosta Mar 10, 2026
e9cb9b6
chore(storybook): upgrade from 8.6 to 10.2
talissoncosta Mar 10, 2026
3e587b9
feat(storybook): add Flagsmith branding and dark theme to manager
talissoncosta Mar 10, 2026
6b98f2d
feat(tokens): add interactive surface and feedback hover/active tokens
talissoncosta Mar 10, 2026
d42d171
refactor(storybook): make token and palette stories fully dynamic
talissoncosta Mar 10, 2026
1f9517f
fix(storybook): fix code tag visibility and palette swatch layout
talissoncosta Mar 10, 2026
2ee13ec
fix(ci): add --legacy-peer-deps to frontend PR workflow
talissoncosta Mar 10, 2026
09b3b44
revert: keep Storybook on v8 until TypeScript is upgraded
talissoncosta Mar 10, 2026
464bd2e
refactor(storybook): extract inline styles into shared docs components
talissoncosta Mar 10, 2026
32ba93a
fix(ci): clean up Chromatic workflow and add useEffect comment
talissoncosta Mar 10, 2026
ecc9084
deps(storybook): upgrade from v8 to v10.3
talissoncosta Mar 24, 2026
226e919
refactor(storybook): migrate config and imports to v10 conventions
talissoncosta Mar 24, 2026
3351ab2
refactor(storybook): move stories from stories/ to documentation/
talissoncosta Mar 24, 2026
f454a60
feat(storybook): add shared Swatch component for colour documentation
talissoncosta Mar 26, 2026
1daa7ba
fix(storybook): fix CategoricalPalette CJS blocker and add colour names
talissoncosta Mar 26, 2026
43df8cf
feat(storybook): add 'How to create a semantic token' guide
talissoncosta Mar 26, 2026
7b32e00
feat(storybook): add Button component stories
talissoncosta Mar 26, 2026
1930977
fix(storybook): fix invisible text in light mode across doc pages
talissoncosta Mar 26, 2026
4182a3c
feat(banner): add Banner component with variant-driven icons and colours
talissoncosta Mar 26, 2026
216592a
refactor(banner): move to folder structure with dedicated scss
talissoncosta Mar 26, 2026
000ce94
fix(storybook): define E2E global to fix Switch component rendering
talissoncosta Mar 27, 2026
a8ce47f
feat(storybook): add Switch component stories
talissoncosta Mar 27, 2026
75058d1
feat(setting-row): add SettingRow pattern component
talissoncosta Mar 27, 2026
9363282
feat(storybook): add Setting pattern stories
talissoncosta Mar 27, 2026
0d5ccfe
fix(setting-row): add ARIA labels and React 16 compatible IDs
talissoncosta Mar 27, 2026
fc240ff
fix(setting-row): align layout with existing settings pattern
talissoncosta Mar 27, 2026
132de14
feat(settings): add Require Feature Ownership setting (#4432)
talissoncosta Mar 27, 2026
d468d9e
feat(skeleton): add Skeleton component with Storybook documentation
talissoncosta Mar 27, 2026
95d8ef8
fix(storybook): add theme support for docs pages
talissoncosta Mar 27, 2026
d1722e2
docs(skeleton): add MDX documentation page
talissoncosta Mar 27, 2026
39a19b6
chore(storybook): remove redundant Dark mode support story from Skeleton
talissoncosta Mar 27, 2026
2326e77
feat(storybook): add Typography documentation — current scale, issues…
talissoncosta Mar 27, 2026
42e7f42
fix(storybook): update Typography docs with base-4 scale and non-stan…
talissoncosta Mar 27, 2026
1a99a8c
feat(storybook): add presentation pages for eng meeting
talissoncosta Mar 27, 2026
e99f40b
feat(storybook): add Chromatic & Storybook MCP presentation page
talissoncosta Mar 27, 2026
d7eb0e6
feat(storybook): add FAQ & Maintenance presentation page
talissoncosta Mar 27, 2026
8435b9e
feat(storybook): add 'What you get for free' section to FAQ page
talissoncosta Mar 27, 2026
d737a99
refactor(storybook): extract 'Why stories matter' into dedicated story
talissoncosta Mar 27, 2026
c528ba0
refactor(storybook): reorder presentation and reframe competitor comp…
talissoncosta Mar 27, 2026
8f9342e
feat(storybook): add component isolation benefit to 'Why stories matter'
talissoncosta Mar 27, 2026
1894169
fix(storybook): clarify '5 purples' in FAQ answer
talissoncosta Mar 27, 2026
a0909ad
feat(storybook): add missing audit findings to presentation
talissoncosta Mar 27, 2026
374dd02
fix(storybook): clarify Chromatic cost with CI path filters and --onl…
talissoncosta Mar 27, 2026
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
45 changes: 45 additions & 0 deletions .github/workflows/frontend-chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Frontend Chromatic

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- frontend/**
- .github/workflows/frontend-chromatic.yml

permissions:
contents: read

jobs:
chromatic:
name: Chromatic
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false

defaults:
run:
working-directory: frontend

steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: frontend/.nvmrc
cache: npm
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
run: npm ci

- name: Publish to Chromatic
uses: chromaui/action@v11
with:
workingDir: frontend
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
exitZeroOnChanges: true
exitOnceUploaded: true
onlyChanged: true
1 change: 1 addition & 0 deletions frontend/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'plugin:@dword-design/import-alias/recommended',
'plugin:storybook/recommended',
],
'globals': {
'$': true,
Expand Down
3 changes: 3 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ common/project.js
# Playwright
e2e/playwright-report/
e2e/test-results/

*storybook.log
storybook-static
104 changes: 104 additions & 0 deletions frontend/.storybook/docs-theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// =============================================================================
// Storybook Docs Theme Overrides
// =============================================================================
// The Storybook docs renderer uses its own styling that doesn't respond to
// our theme toggle. These overrides ensure documentation pages are readable
// in both light and dark mode.
// =============================================================================

// Light mode — fix invisible inline code
.sbdocs code {
color: #1a2634;
}

.dark {
// Docs page background
.sbdocs-wrapper {
background-color: var(--color-surface-default, #101628);
color: var(--color-text-default, #ffffff);
}

.sbdocs-content {
color: var(--color-text-default, #ffffff);
}

// Headings
.sbdocs h1,
.sbdocs h2,
.sbdocs h3,
.sbdocs h4,
.sbdocs h5 {
color: var(--color-text-default, #ffffff);
}

// Body text
.sbdocs p,
.sbdocs li,
.sbdocs td,
.sbdocs th {
color: var(--color-text-default, #ffffff);
}

// Code — inline and blocks
.sbdocs code {
background-color: var(--color-surface-emphasis, #202839);
color: var(--color-text-default, #ffffff);
}

.sbdocs pre {
background-color: var(--color-surface-emphasis, #202839);
color: var(--color-text-default, #ffffff);
}

// Storybook Source/code block container and all its children
.docblock-source,
.docblock-source > * {
background-color: var(--color-surface-emphasis, #202839) !important;
color: var(--color-text-default, #ffffff) !important;
}

// Copy button inside code blocks
.docblock-source button {
background-color: var(--color-surface-muted, #161d30) !important;
color: var(--color-text-default, #ffffff) !important;
border-color: var(--color-border-default, rgba(255, 255, 255, 0.16)) !important;
}

// Syntax highlighting tokens
.docblock-source .token {
color: var(--color-text-default, #ffffff) !important;
}

// Horizontal rules
.sbdocs hr {
border-color: var(--color-border-default, rgba(255, 255, 255, 0.16));
}

// Canvas (story preview) background
.docs-story {
background-color: var(--color-surface-default, #101628);
}

// Table borders
.sbdocs table {
border-color: var(--color-border-default, rgba(255, 255, 255, 0.16));
}

.sbdocs th,
.sbdocs td {
border-color: var(--color-border-default, rgba(255, 255, 255, 0.16));
}

// Table row backgrounds (override Storybook's alternating white rows)
.sbdocs tr {
background-color: var(--color-surface-default, #101628);
}

.sbdocs tr:nth-child(even) {
background-color: var(--color-surface-subtle, #15192b);
}

.sbdocs thead tr {
background-color: var(--color-surface-muted, #161d30);
}
}
70 changes: 70 additions & 0 deletions frontend/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const path = require('path')
const webpack = require('webpack')

/** @type { import('storybook').StorybookConfig } */
const config = {
stories: [
'../documentation/**/*.mdx',
'../documentation/**/*.stories.@(js|jsx|ts|tsx)',
],
staticDirs: ['../web'],
addons: [
'@storybook/addon-webpack5-compiler-swc',
'@storybook/addon-docs',
'@storybook/addon-a11y',
],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
swc: () => ({
jsc: {
transform: {
react: {
runtime: 'automatic',
},
},
parser: {
syntax: 'typescript',
tsx: true,
},
},
}),
webpackFinal: async (config) => {
config.resolve = config.resolve || {}
config.resolve.alias = {
...config.resolve.alias,
common: path.resolve(__dirname, '../common'),
components: path.resolve(__dirname, '../web/components'),
project: path.resolve(__dirname, '../web/project'),
}

config.module = config.module || {}
config.module.rules = config.module.rules || []
config.module.rules.push({
test: /\.scss$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { importLoaders: 1 } },
{
loader: 'sass-loader',
options: {
sassOptions: {
silenceDeprecations: ['slash-div'],
},
},
},
],
})

config.plugins = config.plugins || []
config.plugins.push(
new webpack.DefinePlugin({
E2E: false,
}),
)

return config
},
}
module.exports = config
6 changes: 6 additions & 0 deletions frontend/.storybook/manager-head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<style>
.sidebar-header img {
max-width: 28px;
max-height: 28px;
}
</style>
44 changes: 44 additions & 0 deletions frontend/.storybook/manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { addons } from 'storybook/manager-api'
import { create } from 'storybook/theming'

// Primitive palette — mirrors _primitives.scss
// Storybook manager runs outside the app, so CSS vars aren't available.
const slate = {
200: '#e0e3e9',
300: '#9da4ae',
850: '#161d30',
900: '#15192b',
}
const purple = { 600: '#6837fc' }

addons.setConfig({
theme: create({
base: 'dark',
brandTitle: 'Flagsmith',
brandUrl: 'https://flagsmith.com',
brandImage: '/static/images/nav-logo.png',
brandTarget: '_blank',

// Sidebar
appBg: slate[900],
appContentBg: slate[850],
appBorderColor: 'rgba(255, 255, 255, 0.1)',

// Typography
fontBase: '"Inter", sans-serif',

// Toolbar
barBg: slate[900],
barTextColor: slate[300],
barSelectedColor: purple[600],

// Colours
colorPrimary: purple[600],
colorSecondary: purple[600],

// Text
textColor: slate[200],
textMutedColor: slate[300],
textInverseColor: slate[900],
}),
})
48 changes: 48 additions & 0 deletions frontend/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import '../web/styles/styles.scss'
import './docs-theme.scss'

/** @type { import('storybook').Preview } */
const preview = {
globalTypes: {
theme: {
description: 'Dark mode toggle',
toolbar: {
title: 'Theme',
icon: 'moon',
items: [
{ value: 'light', title: 'Light', icon: 'sun' },
{ value: 'dark', title: 'Dark', icon: 'moon' },
],
dynamicTitle: true,
},
},
},
initialGlobals: {
theme: 'light',
},
decorators: [
(Story, context) => {
const theme = context.globals.theme || 'light'
const isDark = theme === 'dark'

document.documentElement.setAttribute(
'data-bs-theme',
isDark ? 'dark' : 'light',
)
document.body.classList.toggle('dark', isDark)

return Story()
},
],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
backgrounds: { disable: true },
},
}

export default preview
1 change: 1 addition & 0 deletions frontend/common/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type UpdateProjectBody = {
stale_flags_limit_days?: number | null
only_allow_lower_case_feature_names?: boolean
feature_name_regex?: string | null
require_feature_owners?: boolean
}

export type UpdateOrganisationBody = {
Expand Down
1 change: 1 addition & 0 deletions frontend/common/types/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ export type Project = {
total_segments?: number
only_allow_lower_case_feature_names?: boolean
feature_name_regex?: string | null
require_feature_owners?: boolean
environments: Environment[]
}
export type ImportStrategy = 'SKIP' | 'OVERWRITE_DESTRUCTIVE'
Expand Down
Loading
Loading