diff --git a/.changeset/all-camels-agree.md b/.changeset/all-camels-agree.md
new file mode 100644
index 0000000000..44d8a4f2c9
--- /dev/null
+++ b/.changeset/all-camels-agree.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fix broken external links in Backstage UI Header component.
diff --git a/.changeset/angry-sites-fold.md b/.changeset/angry-sites-fold.md
deleted file mode 100644
index 8124ddaf47..0000000000
--- a/.changeset/angry-sites-fold.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-'@backstage/plugin-auth-backend': patch
----
-
-Added the configuration flag `auth.omitIdentityTokenOwnershipClaim` that causes issued user tokens to no longer contain the `ent` claim that represents the ownership references of the user.
-
-The benefit of this new flag is that issued user tokens will be much smaller in
-size, but they will no longer be self-contained. This means that any consumers
-of the token that require access to the ownership claims now need to call the
-`/api/auth/v1/userinfo` endpoint instead. Within the Backstage ecosystem this is
-done automatically, as clients will still receive the full set of claims during
-authentication, while plugin backends will need to use the `UserInfoService`
-which already calls the user info endpoint if necessary.
-
-When enabling this flag, it is important that any custom sign-in resolvers directly return the result of the sign-in method. For example, the following would not work:
-
-```ts
-const { token } = await ctx.issueToken({
- claims: { sub: entityRef, ent: [entityRef] },
-});
-return { token }; // WARNING: This will not work with the flag enabled
-```
-
-Instead, the sign-in resolver should directly return the result:
-
-```ts
-return ctx.issueToken({
- claims: { sub: entityRef, ent: [entityRef] },
-});
-```
diff --git a/.changeset/beige-kiwis-flow.md b/.changeset/beige-kiwis-flow.md
deleted file mode 100644
index 500095b44b..0000000000
--- a/.changeset/beige-kiwis-flow.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-kubernetes-node': minor
----
-
-**BREAKING**: The `PinnipedHelper` class now expects a regular `LoggerService` instance from the new backend system, instead of a Winston logger.
diff --git a/.changeset/better-hats-cross.md b/.changeset/better-hats-cross.md
new file mode 100644
index 0000000000..99444b7fbf
--- /dev/null
+++ b/.changeset/better-hats-cross.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': patch
+---
+
+Adjusted the log line wording of task worker starting
diff --git a/.changeset/better-steaks-act.md b/.changeset/better-steaks-act.md
new file mode 100644
index 0000000000..b3ce601245
--- /dev/null
+++ b/.changeset/better-steaks-act.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+The templates executed with the `yarn new` command now supports templating filenames.
diff --git a/.changeset/brave-donuts-sink.md b/.changeset/brave-donuts-sink.md
deleted file mode 100644
index 47827345a5..0000000000
--- a/.changeset/brave-donuts-sink.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/test-utils': patch
----
-
-Added support for interpolating JSX elements with the `MockTranslationApi`.
diff --git a/.changeset/brave-eggs-mate.md b/.changeset/brave-eggs-mate.md
deleted file mode 100644
index 73eb8e363a..0000000000
--- a/.changeset/brave-eggs-mate.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/theme': patch
----
-
-Show arrow when MuiTableSortLabel receives focus
diff --git a/.changeset/brave-pandas-beam.md b/.changeset/brave-pandas-beam.md
deleted file mode 100644
index 8dd525d749..0000000000
--- a/.changeset/brave-pandas-beam.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-'@backstage/core-app-api': minor
----
-
-Support custom `AuthConnector` for `OAuth2`.
-
-A user can pass their own `AuthConnector` implementation in `OAuth2` constructor.
-In which case the session manager will use that instead of the `DefaultAuthConnector` to interact with the
-authentication provider.
-
-A custom `AuthConnector` may call the authentication provider from the front-end, store and retrieve tokens
-in the session storage, for example, and otherwise send custom requests to the authentication provider and
-handle its responses.
-
-Note, that if the custom `AuthConnector` transforms scopes returned from the authentication provider,
-the transformation must be the same as `OAuth2CreateOptions#scopeTransform` passed to `OAuth2` constructor.
-See creating `DefaultAuthConnector` in `OAuth2#create(...)` for an example.
diff --git a/.changeset/brave-toes-switch.md b/.changeset/brave-toes-switch.md
deleted file mode 100644
index 27b39cccd3..0000000000
--- a/.changeset/brave-toes-switch.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-msgraph': minor
----
-
-Add new `userGroupMember.path`, `user.path` and, `group.path` option to each query type to allow more complex msgraph queries
diff --git a/.changeset/breezy-hotels-deny.md b/.changeset/breezy-hotels-deny.md
deleted file mode 100644
index 13c8b47a7f..0000000000
--- a/.changeset/breezy-hotels-deny.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/integration': patch
----
-
-Added missing `organizations` property to `azure` section in `config.d.ts` file
diff --git a/.changeset/breezy-times-ring.md b/.changeset/breezy-times-ring.md
new file mode 100644
index 0000000000..f65dd6ff55
--- /dev/null
+++ b/.changeset/breezy-times-ring.md
@@ -0,0 +1,5 @@
+---
+'@backstage/repo-tools': patch
+---
+
+Updated knip-reports to detect dependencies in dev/alpha pattern
diff --git a/.changeset/bright-ears-send.md b/.changeset/bright-ears-send.md
new file mode 100644
index 0000000000..5104f2377b
--- /dev/null
+++ b/.changeset/bright-ears-send.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-backend': minor
+---
+
+Enable YAML merge keys in yamlPlaceholderResolver
diff --git a/.changeset/bright-moles-sort.md b/.changeset/bright-moles-sort.md
deleted file mode 100644
index 9796729d1a..0000000000
--- a/.changeset/bright-moles-sort.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-dynamic-feature-service': minor
----
-
-**BREAKING** Removed support for the legacy backend, please migrate to the new backend system
diff --git a/.changeset/bumpy-showers-design.md b/.changeset/bumpy-showers-design.md
deleted file mode 100644
index 2db5006944..0000000000
--- a/.changeset/bumpy-showers-design.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-gitlab': patch
----
-
-If the commit action is not `create` log a more appropriate error message to the end user advising that the files they're trying to modify might not exist
diff --git a/.changeset/busy-badgers-hang.md b/.changeset/busy-badgers-hang.md
deleted file mode 100644
index fc4b0e907e..0000000000
--- a/.changeset/busy-badgers-hang.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/create-app': patch
----
-
-Removed `lerna-debug.log*` pattern from `.gitignore` as Lerna was removed from the package in version `@backstage/create-app@0.5.19`.
diff --git a/.changeset/busy-goats-create.md b/.changeset/busy-goats-create.md
new file mode 100644
index 0000000000..78b8f65571
--- /dev/null
+++ b/.changeset/busy-goats-create.md
@@ -0,0 +1,10 @@
+---
+'@backstage/plugin-search-backend-module-elasticsearch': patch
+'@backstage/plugin-kubernetes-backend': patch
+'@backstage/plugin-techdocs-backend': patch
+'@backstage/plugin-signals-backend': patch
+'@backstage/plugin-techdocs': patch
+'@backstage/plugin-signals': patch
+---
+
+Removed/moved unused dependencies
diff --git a/.changeset/calm-toys-occur.md b/.changeset/calm-toys-occur.md
deleted file mode 100644
index ea03aaf4f8..0000000000
--- a/.changeset/calm-toys-occur.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-'@backstage/plugin-bitbucket-cloud-common': minor
----
-
-Update Bitbucket Cloud schema and models.
-
-The latest schema was fetched from Bitbucket Cloud and stored locally.
-Based on the updated schema, the models got regenerated.
-
-**BREAKING:**
-
-Due to the schema changes, the model update includes one breaking change:
-
-- `Account.username` was removed.
-
-Additionally, there were a couple of compatible changes including the addition of
-`BaseCommit.committer` and others.
diff --git a/.changeset/chatty-months-grow.md b/.changeset/chatty-months-grow.md
deleted file mode 100644
index 1495e7732e..0000000000
--- a/.changeset/chatty-months-grow.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/canon': patch
----
-
-Use correct colour token for TextField clear button icon, prevent layout shift whenever it is hidden or shown and properly size focus area around it. Also stop leading icon shrinking when used together with clear button.
diff --git a/.changeset/chatty-showers-cheat.md b/.changeset/chatty-showers-cheat.md
deleted file mode 100644
index 24c2720fb6..0000000000
--- a/.changeset/chatty-showers-cheat.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend': minor
----
-
-**BREAKING ALPHA**: You can no longer import the catalog plugin from the `/alpha` export; please use the regular root default export instead.
diff --git a/.changeset/chilly-trams-cheer.md b/.changeset/chilly-trams-cheer.md
deleted file mode 100644
index 0e14423ef8..0000000000
--- a/.changeset/chilly-trams-cheer.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-kubernetes-react': patch
----
-
-Add headlamp formatter
diff --git a/.changeset/chubby-cougars-run.md b/.changeset/chubby-cougars-run.md
deleted file mode 100644
index deb954ef6b..0000000000
--- a/.changeset/chubby-cougars-run.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/plugin-catalog-backend': patch
----
-
-This patch addresses an issue identified in Backstage when configured with a MySQL database. If an entity of type location
-(e..all.yaml) has more than 70 referenced entities, clicking "Refresh" does not update the referenced entities as expected. This occurs because the TEXT type in MySQL has a limit of 65,535 bytes, which is insufficient to store all the referenced entities, causing the refresh operation to fail.
diff --git a/.changeset/chubby-needles-vanish.md b/.changeset/chubby-needles-vanish.md
deleted file mode 100644
index 2cd664dcdd..0000000000
--- a/.changeset/chubby-needles-vanish.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/canon': patch
----
-
-Fix Canon missing dependencies
diff --git a/.changeset/clean-otters-allow.md b/.changeset/clean-otters-allow.md
deleted file mode 100644
index 5e0aeb598f..0000000000
--- a/.changeset/clean-otters-allow.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-fix: enable `lazyCompilation` and `refreshOptions` for rspack
diff --git a/.changeset/clever-boats-clap.md b/.changeset/clever-boats-clap.md
new file mode 100644
index 0000000000..6b40f05a76
--- /dev/null
+++ b/.changeset/clever-boats-clap.md
@@ -0,0 +1,8 @@
+---
+'@backstage/plugin-search-react': minor
+'@backstage/plugin-techdocs': minor
+'@backstage/plugin-catalog': minor
+'@backstage/plugin-search': minor
+---
+
+Add support for customizable icons in `SearchResultListItemBlueprint` and related components
diff --git a/.changeset/common-goats-raise.md b/.changeset/common-goats-raise.md
deleted file mode 100644
index c2d797b719..0000000000
--- a/.changeset/common-goats-raise.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-github': patch
----
-
-Use action context logger in Octokit client
diff --git a/.changeset/cool-bikes-push.md b/.changeset/cool-bikes-push.md
deleted file mode 100644
index 00825c1ad0..0000000000
--- a/.changeset/cool-bikes-push.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-app-api': patch
----
-
-Updated `I18nextTranslationApi` to support interpolation of JSX elements.
diff --git a/.changeset/cool-cities-grab.md b/.changeset/cool-cities-grab.md
deleted file mode 100644
index 372ca9887f..0000000000
--- a/.changeset/cool-cities-grab.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-components': patch
----
-
-`LogViewer` now supports a `textWrap` prop that wraps log lines to the next line for overflowing content instead of using horizontal scroll
diff --git a/.changeset/cool-colts-float.md b/.changeset/cool-colts-float.md
deleted file mode 100644
index c001f81ade..0000000000
--- a/.changeset/cool-colts-float.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/repo-tools': patch
----
-
-Support passing additional properties to OpenAPI server generator
diff --git a/.changeset/cool-groups-fail.md b/.changeset/cool-groups-fail.md
deleted file mode 100644
index 459358676b..0000000000
--- a/.changeset/cool-groups-fail.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-'@backstage/backend-defaults': patch
----
-
-`GithubUrlReader`'s search detects glob-patterns supported by `minimatch`, instead of just detecting
-`*` and `?` characters.
-
-For example, this allows to search for patterns like `{C,c}atalog-info.yaml`.
diff --git a/.changeset/cool-knives-design.md b/.changeset/cool-knives-design.md
deleted file mode 100644
index 63197edd16..0000000000
--- a/.changeset/cool-knives-design.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/frontend-plugin-api': patch
----
-
-The `id` option of `createFrontendPlugin` has been renamed to `pluginId` in order to better align with similar APIs in the frontend and backend systems.
-
-The old `id` option is deprecated and will be removed in a future release.
diff --git a/.changeset/crazy-chefs-sin.md b/.changeset/crazy-chefs-sin.md
deleted file mode 100644
index 8d33add929..0000000000
--- a/.changeset/crazy-chefs-sin.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-notifications-backend-module-slack': patch
----
-
-Fix slack notification processor to handle a notification with an empty description
diff --git a/.changeset/create-app-1745936753.md b/.changeset/create-app-1745936753.md
deleted file mode 100644
index b50d431d4b..0000000000
--- a/.changeset/create-app-1745936753.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/create-app': patch
----
-
-Bumped create-app version.
diff --git a/.changeset/create-app-1747144256.md b/.changeset/create-app-1747144256.md
deleted file mode 100644
index b50d431d4b..0000000000
--- a/.changeset/create-app-1747144256.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/create-app': patch
----
-
-Bumped create-app version.
diff --git a/.changeset/create-app-1745325336.md b/.changeset/create-app-1761312116.md
similarity index 100%
rename from .changeset/create-app-1745325336.md
rename to .changeset/create-app-1761312116.md
diff --git a/.changeset/cruel-items-dig.md b/.changeset/cruel-items-dig.md
new file mode 100644
index 0000000000..65f07956ac
--- /dev/null
+++ b/.changeset/cruel-items-dig.md
@@ -0,0 +1,27 @@
+---
+'@backstage/ui': minor
+---
+
+**BREAKING**: Migrated Avatar component from Base UI to custom implementation with size changes:
+
+- Base UI-specific props are no longer supported
+- Size values have been updated:
+ - New `x-small` size added (1.25rem / 20px)
+ - `small` size unchanged (1.5rem / 24px)
+ - `medium` size unchanged (2rem / 32px, default)
+ - `large` size **changed from 3rem to 2.5rem** (40px)
+ - New `x-large` size added (3rem / 48px)
+
+Migration:
+
+```diff
+# Remove Base UI-specific props
+-
++
+
+# Update large size usage to x-large for same visual size
+-
++
+```
+
+Added `purpose` prop for accessibility control (`'informative'` or `'decoration'`).
diff --git a/.changeset/cruel-lights-sip.md b/.changeset/cruel-lights-sip.md
deleted file mode 100644
index b38ab052a9..0000000000
--- a/.changeset/cruel-lights-sip.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-auth-backend-module-github-provider': patch
----
-
-Added missing types package
diff --git a/.changeset/cruel-plums-talk.md b/.changeset/cruel-plums-talk.md
new file mode 100644
index 0000000000..bc7a30b048
--- /dev/null
+++ b/.changeset/cruel-plums-talk.md
@@ -0,0 +1,58 @@
+---
+'@backstage/ui': minor
+---
+
+**BREAKING**: Migrated Checkbox component from Base UI to React Aria Components.
+
+API changes required:
+
+- `checked` → `isSelected`
+- `defaultChecked` → `defaultSelected`
+- `disabled` → `isDisabled`
+- `required` → `isRequired`
+- `label` prop removed - use `children` instead
+- CSS: `bui-CheckboxLabel` class removed
+- Data attribute: `data-checked` → `data-selected`
+- Use without label is no longer supported
+
+Migration examples:
+
+Before:
+
+```tsx
+
+```
+
+After:
+
+```tsx
+
+ Accept terms
+
+```
+
+Before:
+
+```tsx
+
+```
+
+After:
+
+```tsx
+Option
+```
+
+Before:
+
+```tsx
+
+```
+
+After:
+
+```tsx
+
+ Accessible label
+
+```
diff --git a/.changeset/cyan-pots-appear.md b/.changeset/cyan-pots-appear.md
deleted file mode 100644
index f82f4548c3..0000000000
--- a/.changeset/cyan-pots-appear.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-unprocessed': minor
----
-
-**BREAKING** Removed support for the legacy backend and removed references to `@backstage/backend-common`, please [migrate to the new backend system](https://backstage.io/docs/backend-system/building-plugins-and-modules/migrating)
diff --git a/.changeset/deep-gifts-slide.md b/.changeset/deep-gifts-slide.md
new file mode 100644
index 0000000000..f3be77e74d
--- /dev/null
+++ b/.changeset/deep-gifts-slide.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed CSS issues in Select component including popover width constraints, focus outline behavior, and overflow handling.
diff --git a/.changeset/deep-ties-move.md b/.changeset/deep-ties-move.md
deleted file mode 100644
index 57f6c7772c..0000000000
--- a/.changeset/deep-ties-move.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-ldap': patch
----
-
-Improves error reporting for missing metadata.name in LDAP catalog provider.
diff --git a/.changeset/dirty-grapes-vanish.md b/.changeset/dirty-grapes-vanish.md
deleted file mode 100644
index b78660d21e..0000000000
--- a/.changeset/dirty-grapes-vanish.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/backend-defaults': minor
----
-
-**BREAKING**: The `DefaultSchedulerService` constructor options now requires `RootLifecycleService`, `HttpRouterService`, and `PluginMetadataService` fields.
-
-The scheduler will register a REST API for listing and triggering tasks. Please see [the scheduler documentation](https://backstage.io/docs/backend-system/core-services/scheduler) for more details about this API.
diff --git a/.changeset/dry-carpets-hope.md b/.changeset/dry-carpets-hope.md
deleted file mode 100644
index 569e089a66..0000000000
--- a/.changeset/dry-carpets-hope.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-backstage-openapi': patch
----
-
-Do not swallow errors; instead allow them to bubble up to the task scheduler for better tracking and logging.
diff --git a/.changeset/dry-shirts-film.md b/.changeset/dry-shirts-film.md
deleted file mode 100644
index 35ada3969a..0000000000
--- a/.changeset/dry-shirts-film.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-home': patch
----
-
-Added the Catalog presentation API to the HomePageRecentlyVisited and HomePageTopVisited components
diff --git a/.changeset/dull-doodles-trade.md b/.changeset/dull-doodles-trade.md
deleted file mode 100644
index eb9399fcc7..0000000000
--- a/.changeset/dull-doodles-trade.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-kubernetes-backend': patch
----
-
-Changed logging of cluster details to debug to minimise log clutter.
diff --git a/.changeset/early-colts-accept.md b/.changeset/early-colts-accept.md
deleted file mode 100644
index ff583144f0..0000000000
--- a/.changeset/early-colts-accept.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/backend-dynamic-feature-service': patch
----
-
-Fixed various typos.
-`FrontendRemoteResolver`'s misspelled `getAdditionaRemoteInfo` has been deprecated. Use the correct spelling `getAdditionalRemoteInfo` instead.
diff --git a/.changeset/early-dryers-teach.md b/.changeset/early-dryers-teach.md
deleted file mode 100644
index d747fbc730..0000000000
--- a/.changeset/early-dryers-teach.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-node': minor
----
-
-Added `parseEntityYaml` from `@backstage/plugin-catalog-backend`, to make it more easily usable by custom plugins and modules
diff --git a/.changeset/easy-hands-grow.md b/.changeset/easy-hands-grow.md
new file mode 100644
index 0000000000..fc3cfd96e1
--- /dev/null
+++ b/.changeset/easy-hands-grow.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-catalog-backend': minor
+'@backstage/plugin-catalog-node': minor
+---
+
+Make YAML merge (<<:) support configurable in the Backstage Catalog instead of always being enabled
diff --git a/.changeset/eight-toys-feel.md b/.changeset/eight-toys-feel.md
deleted file mode 100644
index c0f5a12c40..0000000000
--- a/.changeset/eight-toys-feel.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-'@backstage/backend-app-api': patch
----
-
-Added a configuration to permit backend plugin module failures on startup:
-
-```yaml
-backend:
- ...
- startup:
- plugins:
- plugin-x:
- modules:
- module-y:
- onPluginModuleBootFailure: continue
-```
-
-This configuration permits `plugin-x` with `module-y` to fail on startup. Omitting the
-`onPluginModuleBootFailure` configuration matches the previous behavior, wherein any
-individual plugin module failure is forwarded to the plugin and aborts backend startup.
-
-The default can also be changed, so that continuing on failure is the default
-unless otherwise specified:
-
-```yaml
-backend:
- startup:
- default:
- onPluginModuleBootFailure: continue
- plugins:
- catalog:
- modules:
- github:
- onPluginModuleBootFailure: abort
-```
diff --git a/.changeset/eighty-results-prove.md b/.changeset/eighty-results-prove.md
new file mode 100644
index 0000000000..6df2d2f11a
--- /dev/null
+++ b/.changeset/eighty-results-prove.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-app-visualizer': patch
+---
+
+Improve tree visualizer to use a horizontal layout and fill the content space.
diff --git a/.changeset/eleven-beans-relax.md b/.changeset/eleven-beans-relax.md
deleted file mode 100644
index fd01394d2d..0000000000
--- a/.changeset/eleven-beans-relax.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-'@backstage/release-manifests': patch
----
-
-This expands the configurability of `release-manifests` to pave the road for more configuration options in the `cli`.
-
-Specifically it allows the specification of mirrored, proxied, or air-gapped hosts when upgrading across releases when
-working in restricted or heavily governed development environments (common in large enterprises and government
-entities).
diff --git a/.changeset/eleven-carpets-win.md b/.changeset/eleven-carpets-win.md
new file mode 100644
index 0000000000..69d677786a
--- /dev/null
+++ b/.changeset/eleven-carpets-win.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': patch
+---
+
+Fix a bug in the Gitlab URL reader where `search` did not handle multiple globs
diff --git a/.changeset/eleven-ghosts-strive.md b/.changeset/eleven-ghosts-strive.md
deleted file mode 100644
index ac48c19524..0000000000
--- a/.changeset/eleven-ghosts-strive.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder': patch
----
-
-Full support in EntityPicker (and derivatives) for default EntityPresentationApi
diff --git a/.changeset/every-ants-count.md b/.changeset/every-ants-count.md
new file mode 100644
index 0000000000..963c3a521a
--- /dev/null
+++ b/.changeset/every-ants-count.md
@@ -0,0 +1,7 @@
+---
+'@backstage/backend-test-utils': minor
+---
+
+Added support for Postgres 18 to the available `TestDatabases`.
+
+Note that the set of _default_ databases to test against for users of the `TestDatabases` class was also updated to include Postgres 14 and 18, instead of 13 and 17. If you need to override this, you can pass in an explicit `ids` argument, for example `ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3']`.
diff --git a/.changeset/every-bottles-grow.md b/.changeset/every-bottles-grow.md
deleted file mode 100644
index a61a7b42b9..0000000000
--- a/.changeset/every-bottles-grow.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend': patch
----
-
-"Added a note clarifying that `entity-fetch` audit events are not visible by default in the logs and are only displayed when the log severity level is adjusted."
diff --git a/.changeset/every-clocks-arrive.md b/.changeset/every-clocks-arrive.md
new file mode 100644
index 0000000000..d2061913e3
--- /dev/null
+++ b/.changeset/every-clocks-arrive.md
@@ -0,0 +1,5 @@
+---
+'@backstage/dev-utils': patch
+---
+
+Fixed styling of the dev app by adding a lazy import of `@backstage/ui/css/styles.css`.
diff --git a/.changeset/famous-cities-stand.md b/.changeset/famous-cities-stand.md
deleted file mode 100644
index 86ba727997..0000000000
--- a/.changeset/famous-cities-stand.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-auth-node': patch
----
-
-Added the `identity` property to `BackstageSignInResult`.
-
-The `prepareBackstageIdentityResponse` function will now also forward the `identity` to the response if present in the provided sign-in result.
diff --git a/.changeset/fancy-frogs-like.md b/.changeset/fancy-frogs-like.md
deleted file mode 100644
index ed3a9fbf81..0000000000
--- a/.changeset/fancy-frogs-like.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Internal code cleanup
diff --git a/.changeset/fast-tools-mate.md b/.changeset/fast-tools-mate.md
new file mode 100644
index 0000000000..2e212ce1b3
--- /dev/null
+++ b/.changeset/fast-tools-mate.md
@@ -0,0 +1,10 @@
+---
+'@backstage/plugin-notifications-backend': patch
+---
+
+Show default settings for notifications even before receiving first notification.
+
+Previously, it was not possible for the users to see or modify their notification settings until they had received at
+least one notification from specific origin or topic.
+This update ensures that default settings are displayed from the outset,
+allowing users to customize their preferences immediately.
diff --git a/.changeset/fine-hands-return.md b/.changeset/fine-hands-return.md
new file mode 100644
index 0000000000..d3bf69c9b9
--- /dev/null
+++ b/.changeset/fine-hands-return.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fix default text color in Backstage UI
diff --git a/.changeset/five-seas-jam.md b/.changeset/five-seas-jam.md
new file mode 100644
index 0000000000..4aa481422f
--- /dev/null
+++ b/.changeset/five-seas-jam.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-notifications-backend-module-email': patch
+---
+
+Updated `AWS SES` client to version 2 to support `nodemailer` version 7.
diff --git a/.changeset/fix-text-truncate-prop.md b/.changeset/fix-text-truncate-prop.md
new file mode 100644
index 0000000000..26aee3280c
--- /dev/null
+++ b/.changeset/fix-text-truncate-prop.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed Text component to prevent `truncate` prop from being spread to the underlying DOM element.
diff --git a/.changeset/flat-paws-do.md b/.changeset/flat-paws-do.md
new file mode 100644
index 0000000000..13d78c1423
--- /dev/null
+++ b/.changeset/flat-paws-do.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-backend-module-openapi': patch
+---
+
+fix wrong dereferencing for AsyncApi 3 documents
diff --git a/.changeset/floppy-days-tell.md b/.changeset/floppy-days-tell.md
deleted file mode 100644
index 2eb184f6fe..0000000000
--- a/.changeset/floppy-days-tell.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/canon': patch
----
-
-For improved a11y, clicking a Select component label now focuses the Select trigger element, and the TextField component's label is now styled to indicate it's interactive.
diff --git a/.changeset/four-peaches-talk.md b/.changeset/four-peaches-talk.md
deleted file mode 100644
index 4b0d2e8a47..0000000000
--- a/.changeset/four-peaches-talk.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Internal refactor of opaque type handling.
diff --git a/.changeset/four-snails-argue.md b/.changeset/four-snails-argue.md
deleted file mode 100644
index 905e52a559..0000000000
--- a/.changeset/four-snails-argue.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-'@backstage/integration': minor
----
-
-Added support for federated credentials using managed identities in the Azure DevOps integration. Federated credentials are only available for Azure DevOps organizations that have been configured to use Entra ID for authentication.
-
-```diff
-integrations:
- azure:
- - host: dev.azure.com
- credentials:
-+ - clientId: ${APP_REGISTRATION_CLIENT_ID}
-+ managedIdentityClientId: system-assigned
-+ tenantId: ${AZURE_TENANT_ID}
-```
-
-This also adds support for automatically using the system-assigned managed identity of an Azure resource by specifying `system-assigned` as the client ID of the managed identity.
-
-```diff
-integrations:
- azure:
- - host: dev.azure.com
- credentials:
-- - clientId: ${AZURE_CLIENT_ID}
-+ - clientId: system-assigned
-```
diff --git a/.changeset/fruity-bags-flow.md b/.changeset/fruity-bags-flow.md
deleted file mode 100644
index bc6034a489..0000000000
--- a/.changeset/fruity-bags-flow.md
+++ /dev/null
@@ -1,23 +0,0 @@
----
-'@backstage/plugin-catalog-unprocessed-entities': patch
-'@backstage/frontend-test-utils': patch
-'@backstage/core-compat-api': patch
-'@backstage/plugin-app-visualizer': patch
-'@backstage/plugin-catalog-import': patch
-'@backstage/plugin-catalog-graph': patch
-'@backstage/plugin-notifications': patch
-'@backstage/plugin-user-settings': patch
-'@backstage/plugin-kubernetes': patch
-'@backstage/plugin-scaffolder': patch
-'@backstage/plugin-api-docs': patch
-'@backstage/plugin-devtools': patch
-'@backstage/plugin-techdocs': patch
-'@backstage/plugin-catalog': patch
-'@backstage/plugin-signals': patch
-'@backstage/plugin-search': patch
-'@backstage/plugin-home': patch
-'@backstage/plugin-app': patch
-'@backstage/plugin-org': patch
----
-
-Internal update to use the new `pluginId` option of `createFrontendPlugin`.
diff --git a/.changeset/fruity-snails-laugh.md b/.changeset/fruity-snails-laugh.md
new file mode 100644
index 0000000000..3b7775efac
--- /dev/null
+++ b/.changeset/fruity-snails-laugh.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-backend-module-gitlab': minor
+---
+
+fix scaffolder action createDeployToken to allow usage of oauth tokens
diff --git a/.changeset/funny-papayas-tell.md b/.changeset/funny-papayas-tell.md
deleted file mode 100644
index 98beaf89e0..0000000000
--- a/.changeset/funny-papayas-tell.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog': minor
----
-
-Show the pagination text for the offset-paginated catalog table, and remove the pagination bar from the top of the `CatalogTable` when pagination is enabled.
diff --git a/.changeset/funny-stars-open.md b/.changeset/funny-stars-open.md
new file mode 100644
index 0000000000..336e68bac1
--- /dev/null
+++ b/.changeset/funny-stars-open.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Fix inconsistent behavior in the `new` command for the `@internal` scope: it now consistently defaults to the `backstage-plugin-` infix whether the `--scope` option is not set or it's set to `internal`.
diff --git a/.changeset/gentle-bikes-relax.md b/.changeset/gentle-bikes-relax.md
new file mode 100644
index 0000000000..7a4aeda5f8
--- /dev/null
+++ b/.changeset/gentle-bikes-relax.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs-module-addons-contrib': patch
+---
+
+ExpandableCollapse Techdocs Addon was breaking native sidebar collapse on Firefox
diff --git a/.changeset/giant-lamps-happen.md b/.changeset/giant-lamps-happen.md
new file mode 100644
index 0000000000..1bc4ae6eab
--- /dev/null
+++ b/.changeset/giant-lamps-happen.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Improved the Link component structure in Backstage UI.
diff --git a/.changeset/good-islands-drive.md b/.changeset/good-islands-drive.md
deleted file mode 100644
index 5b909dc84f..0000000000
--- a/.changeset/good-islands-drive.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Added `info` object to the context of the alpha CLI.
diff --git a/.changeset/green-trainers-float.md b/.changeset/green-trainers-float.md
deleted file mode 100644
index 537fe01736..0000000000
--- a/.changeset/green-trainers-float.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder': patch
----
-
-Fix EntityPicker field to render description as markdown, matching other form components in the system.
diff --git a/.changeset/grumpy-phones-kiss.md b/.changeset/grumpy-phones-kiss.md
deleted file mode 100644
index 96b6ff05d9..0000000000
--- a/.changeset/grumpy-phones-kiss.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-search-backend-module-elasticsearch': patch
----
-
-Fixed an issue where the `search.elasticsearch.queryOptions` config were not picked up by the `ElasticSearchSearchEngine`.
diff --git a/.changeset/grumpy-planes-bet.md b/.changeset/grumpy-planes-bet.md
new file mode 100644
index 0000000000..bf7d36be41
--- /dev/null
+++ b/.changeset/grumpy-planes-bet.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder-react': patch
+---
+
+Fixed a bug in the Scaffolder's template parsing in the `useTemplateSchema` hook by removing the title instead of setting it to `undefined`
diff --git a/.changeset/heavy-baths-rule.md b/.changeset/heavy-baths-rule.md
deleted file mode 100644
index 09f26e8b57..0000000000
--- a/.changeset/heavy-baths-rule.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-github': minor
----
-
-**BREAKING** The `GithubLocationAnalyzer` now requires the `AuthService` and the `CatalogService` when being constructed and the `TokenManger` has been removed.
diff --git a/.changeset/heavy-cars-wash.md b/.changeset/heavy-cars-wash.md
new file mode 100644
index 0000000000..87da5a6d95
--- /dev/null
+++ b/.changeset/heavy-cars-wash.md
@@ -0,0 +1,70 @@
+---
+'@backstage/plugin-catalog-backend-module-ldap': minor
+---
+
+Moved from `ldapjs` dependency to `ldapts`
+
+### Breaking Changes
+
+**Type Migration**
+
+Custom transformers must now accept `Entry` from ldapts instead of `SearchEntry`
+from ldapjs The Entry type provides direct property access without need for
+`.object()` or `.raw()` methods.
+
+If you have custom user or group transformers, update the signature from:
+
+```typescript
+(vendor: LdapVendor, config: UserConfig, entry: SearchEntry) =>
+ Promise;
+```
+
+to
+
+```typescript
+(vendor: LdapVendor, config: UserConfig, entry: Entry) =>
+ Promise;
+```
+
+**Search Options**
+
+Updated LDAP search configuration `typesOnly: false` → `attributeValues: true`
+This inverts the boolean logic: ldapjs used negative form while ldapts uses
+positive form. Both achieve the same result: retrieving attribute values rather
+than just attribute names.
+
+Update LDAP search options in configuration from
+
+```yaml
+options:
+ typesOnly: false
+```
+
+to
+
+```yaml
+options:
+ attributeValues: true
+```
+
+**API Changes** Removed `LdapClient.searchStreaming()` method. Users should
+migrate to `LdapClient.search()` instead
+
+If you're using `searchStreaming` directly:
+
+```typescript
+// Before
+await client.searchStreaming(dn, options, async entry => {
+ // process each entry
+});
+
+// After
+const entries = await client.search(dn, options);
+for (const entry of entries) {
+ // process each entry
+}
+```
+
+> **_NOTE:_**: Both methods have always loaded all entries into memory. The
+> searchStreaming method was only needed internally to handle ldapjs's
+> event-based API.
diff --git a/.changeset/heavy-onions-swim.md b/.changeset/heavy-onions-swim.md
deleted file mode 100644
index 42ba056c1e..0000000000
--- a/.changeset/heavy-onions-swim.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend': minor
----
-
-Added `workspace:template` and `workspace:template:file` actions to complement respective `fetch:*` actions
diff --git a/.changeset/honest-lamps-occur.md b/.changeset/honest-lamps-occur.md
new file mode 100644
index 0000000000..27b2eb44f5
--- /dev/null
+++ b/.changeset/honest-lamps-occur.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed dialog backdrop appearance in dark mode.
diff --git a/.changeset/honest-pandas-win.md b/.changeset/honest-pandas-win.md
new file mode 100644
index 0000000000..a9dc5d7e9d
--- /dev/null
+++ b/.changeset/honest-pandas-win.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-plugin-api': minor
+---
+
+Promote `instanceMetadata` service to main entrypoint.
diff --git a/.changeset/honest-states-repeat.md b/.changeset/honest-states-repeat.md
deleted file mode 100644
index 06b459b673..0000000000
--- a/.changeset/honest-states-repeat.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-home': patch
----
-
-Added optional title prop to `customHomePageGrid`
diff --git a/.changeset/honest-teams-shave.md b/.changeset/honest-teams-shave.md
deleted file mode 100644
index 6afbf37039..0000000000
--- a/.changeset/honest-teams-shave.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-'@backstage/plugin-home-react': patch
-'@backstage/plugin-home': patch
----
-
-Export ContentModal from `@backstage/plugin-home-react` so people can use this in other scenarios.
-Renamed `CatalogReactComponentsNameToClassKey` to `PluginHomeComponentsNameToClassKey` in `overridableComponents.ts`
-
-Made QuickStartCard `docsLinkTitle` prop more flexible to allow for any React.JSX.Element instead of just a string.
-Added QuickStartCard prop `additionalContent` which can eventually replace the prop `video`.
diff --git a/.changeset/huge-olives-do.md b/.changeset/huge-olives-do.md
deleted file mode 100644
index 61eb37559f..0000000000
--- a/.changeset/huge-olives-do.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-events-backend-module-gitlab': patch
----
-
-Adds support for `object_kind` field with priority over `event_name` on Gitlab webhook event types
diff --git a/.changeset/huge-taxis-grab.md b/.changeset/huge-taxis-grab.md
new file mode 100644
index 0000000000..a021fcb82c
--- /dev/null
+++ b/.changeset/huge-taxis-grab.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Migrated CellProfile component from Base UI Avatar to Backstage UI Avatar component.
diff --git a/.changeset/icy-mugs-glow.md b/.changeset/icy-mugs-glow.md
deleted file mode 100644
index 553daff24a..0000000000
--- a/.changeset/icy-mugs-glow.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-bitbucket-cloud-common': patch
----
-
-Add support for `repo:updated` events as `Events.RepoUpdatedEvent`.
diff --git a/.changeset/itchy-bars-smell.md b/.changeset/itchy-bars-smell.md
new file mode 100644
index 0000000000..b82779502e
--- /dev/null
+++ b/.changeset/itchy-bars-smell.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': minor
+---
+
+Fixing styles on SearchField in Backstage UI after migration to CSS modules. `SearchField` has now its own set of class names. We previously used class names from `TextField` but this approach was creating some confusion so going forward in your theme you'll be able to theme `TextField` and `SearchField` separately.
diff --git a/.changeset/khaki-grapes-sink.md b/.changeset/khaki-grapes-sink.md
deleted file mode 100644
index d96521e029..0000000000
--- a/.changeset/khaki-grapes-sink.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/canon': patch
----
-
-Added new icon and onClear props to the TextField to make it easier to accessorize inputs.
diff --git a/.changeset/large-experts-sort.md b/.changeset/large-experts-sort.md
deleted file mode 100644
index 6d58836db7..0000000000
--- a/.changeset/large-experts-sort.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-events-backend-module-github': minor
----
-
-**BREAKING**: Removed the `createGithubSignatureValidator` export.
-
-Added support webhook validation based on `integrations.github.[].apps.[].webhookSecret`.
diff --git a/.changeset/large-lemons-clap.md b/.changeset/large-lemons-clap.md
deleted file mode 100644
index cf1a3eed5d..0000000000
--- a/.changeset/large-lemons-clap.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/backend-plugin-api': patch
-'@backstage/backend-defaults': patch
-'@backstage/integration': patch
----
-
-update documentation
diff --git a/.changeset/lazy-tires-show.md b/.changeset/lazy-tires-show.md
deleted file mode 100644
index 3ccf5f155e..0000000000
--- a/.changeset/lazy-tires-show.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-permission-backend': minor
-'@backstage/plugin-permission-common': minor
-'@backstage/plugin-permission-node': minor
----
-
-Fixed an issue causing the `PermissionClient` to exhaust the request body size limit too quickly when making many requests.
diff --git a/.changeset/legal-weeks-walk.md b/.changeset/legal-weeks-walk.md
new file mode 100644
index 0000000000..2068ff40b2
--- /dev/null
+++ b/.changeset/legal-weeks-walk.md
@@ -0,0 +1,5 @@
+---
+'@backstage/create-app': patch
+---
+
+Added `@backstage/plugin-app-visualizer` to the app in the `--next` template.
diff --git a/.changeset/lemon-spies-sleep.md b/.changeset/lemon-spies-sleep.md
new file mode 100644
index 0000000000..15ec451f62
--- /dev/null
+++ b/.changeset/lemon-spies-sleep.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-react': patch
+---
+
+Apply default ordering of templates
diff --git a/.changeset/long-humans-sink.md b/.changeset/long-humans-sink.md
new file mode 100644
index 0000000000..9959caf1b1
--- /dev/null
+++ b/.changeset/long-humans-sink.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Avatar components in x-small and small sizes now display only one initial instead of two, improving readability at smaller dimensions.
diff --git a/.changeset/loud-carpets-throw.md b/.changeset/loud-carpets-throw.md
new file mode 100644
index 0000000000..169703d815
--- /dev/null
+++ b/.changeset/loud-carpets-throw.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Added a template for the `yarn new` command to create an catalog entity provider. To add this template to an explicit list in the root `package.json`, use `@backstage/cli/templates/catalog-provider-module`.
diff --git a/.changeset/lovely-cats-take.md b/.changeset/lovely-cats-take.md
deleted file mode 100644
index ab1ee1c249..0000000000
--- a/.changeset/lovely-cats-take.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch
----
-
-Support Bitbucket Cloud's `repo:updated` events at `BitbucketCloudEntityProvider`.
-
-To make use of the new event type, you have to configure your webhook or add a new ones
-that delivers this event type to Backstage similar to `repo:push` before.
-
-Only `repo:updated` events that modify a repository's URL (e.g., due to a name change)
-will cause changes (removing the "old", adding the "new" repository).
diff --git a/.changeset/mean-parents-build.md b/.changeset/mean-parents-build.md
deleted file mode 100644
index d3bba29019..0000000000
--- a/.changeset/mean-parents-build.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/canon': patch
----
-
-Pin version of @base-ui-components/react.
diff --git a/.changeset/mighty-carrots-decide.md b/.changeset/mighty-carrots-decide.md
deleted file mode 100644
index 68f210da48..0000000000
--- a/.changeset/mighty-carrots-decide.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-search-react': minor
----
-
-Allow search filters to provide labels and values separately, and not only values
diff --git a/.changeset/moody-plums-add.md b/.changeset/moody-plums-add.md
new file mode 100644
index 0000000000..a88e11b347
--- /dev/null
+++ b/.changeset/moody-plums-add.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-gateway-backend': minor
+---
+
+Update usage of the `instanceMetadata` service.
diff --git a/.changeset/moody-women-itch.md b/.changeset/moody-women-itch.md
deleted file mode 100644
index f68fddb848..0000000000
--- a/.changeset/moody-women-itch.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-notifications-backend-module-email': patch
----
-
-Enable the ability to configure the endpoint for the SES connection used in the notifications email module. This enables the configuration of alternate endpoints as required, for example for local testing or alternative stacks.
diff --git a/.changeset/neat-glasses-occur.md b/.changeset/neat-glasses-occur.md
deleted file mode 100644
index 5fea5b1886..0000000000
--- a/.changeset/neat-glasses-occur.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-import': patch
----
-
-Add i18n support for `catalog-import` plugin.
diff --git a/.changeset/neat-glasses-occured.md b/.changeset/neat-glasses-occured.md
deleted file mode 100644
index 289e03e243..0000000000
--- a/.changeset/neat-glasses-occured.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-import': minor
----
-
-**BREAKING**: `generateStepper` and `defaultGenerateStepper` now require a translation argument to be passed through for supporting translations.
diff --git a/.changeset/new-hands-scream.md b/.changeset/new-hands-scream.md
deleted file mode 100644
index 03291648db..0000000000
--- a/.changeset/new-hands-scream.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-auth-backend': minor
----
-
-**BREAKING**: Removed support for the old backend system, and removed all deprecated exports.
-
-If you were using one of the deprecated imports from this package, you will have to follow the instructions in their respective deprecation notices before upgrading. Most of the general utilities are available from `@backstage/plugin-auth-node`, and the specific auth providers are available from dedicated packages such as for example `@backstage/plugin-auth-backend-module-github-provider`. See [the auth docs](https://backstage.io/docs/auth/) for specific instructions.
diff --git a/.changeset/nice-vans-vanish.md b/.changeset/nice-vans-vanish.md
deleted file mode 100644
index c52fc42488..0000000000
--- a/.changeset/nice-vans-vanish.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/canon': minor
----
-
-**Breaking Change** Icons on Button and IconButton now need to be imported and placed like this: } />
diff --git a/.changeset/ninety-cobras-feel.md b/.changeset/ninety-cobras-feel.md
new file mode 100644
index 0000000000..e287ef33ed
--- /dev/null
+++ b/.changeset/ninety-cobras-feel.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-techdocs': patch
+---
+
+Fixed an issue where the entire TechDocs page would re-render when navigating between pages within the same entity's documentation.
diff --git a/.changeset/ninety-donkeys-shop.md b/.changeset/ninety-donkeys-shop.md
deleted file mode 100644
index 53e78afc6a..0000000000
--- a/.changeset/ninety-donkeys-shop.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-app-api': patch
----
-
-Fixed an issue causing `OAuthRequestDialog` to re-render on mount.
diff --git a/.changeset/old-crews-serve.md b/.changeset/old-crews-serve.md
deleted file mode 100644
index db6bb4cf23..0000000000
--- a/.changeset/old-crews-serve.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-incremental-ingestion': minor
----
-
-**BREAKING** Removed support for the legacy backend, please [migrate to the new backend system](https://github.com/backstage/backstage/tree/v1.38.0/plugins/catalog-backend-module-incremental-ingestion#installation). Also, if you were importing from the `/alpha` export of this package, you should remove the `/alpha` part.
diff --git a/.changeset/old-parents-cut.md b/.changeset/old-parents-cut.md
deleted file mode 100644
index d2fdaac564..0000000000
--- a/.changeset/old-parents-cut.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-github': patch
----
-
-Added filter to include archived repositories
diff --git a/.changeset/open-ghosts-fix.md b/.changeset/open-ghosts-fix.md
deleted file mode 100644
index 144f24dea0..0000000000
--- a/.changeset/open-ghosts-fix.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/canon': patch
----
-
-Fixed an issue with Canon's DataTable.Pagination component showing the wrong number for the "to" count.
diff --git a/.changeset/open-items-open.md b/.changeset/open-items-open.md
new file mode 100644
index 0000000000..12fcefab4a
--- /dev/null
+++ b/.changeset/open-items-open.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-app-api': minor
+---
+
+Updates API for `instanceMetadata` service to return a list of plugins not features.
diff --git a/.changeset/open-lands-shop.md b/.changeset/open-lands-shop.md
deleted file mode 100644
index 5205e83cbe..0000000000
--- a/.changeset/open-lands-shop.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-techdocs': patch
----
-
-Update keyboard focus on when clicking hash links. This fixes the issue where the "skip to content" link rendered by Material MkDocs isn't focused when used.
diff --git a/.changeset/orange-banks-deny.md b/.changeset/orange-banks-deny.md
deleted file mode 100644
index 3719206865..0000000000
--- a/.changeset/orange-banks-deny.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend-module-gitea': minor
----
-
-add new gitea provider module
diff --git a/.changeset/polite-seas-divide.md b/.changeset/polite-seas-divide.md
new file mode 100644
index 0000000000..e265e6df90
--- /dev/null
+++ b/.changeset/polite-seas-divide.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fix the default font size in Backstage UI.
diff --git a/.changeset/polite-shoes-allow.md b/.changeset/polite-shoes-allow.md
deleted file mode 100644
index b7e06bce6f..0000000000
--- a/.changeset/polite-shoes-allow.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/frontend-app-api': patch
----
-
-Added the ability to ignore unknown extension config by passing `{ flags: { allowUnknownExtensionConfig: true } }` to `createSpecializedApp`.
diff --git a/.changeset/pre.json b/.changeset/pre.json
index fe254886cd..a8ef8fe974 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -2,332 +2,271 @@
"mode": "pre",
"tag": "next",
"initialVersions": {
- "example-app": "0.2.108",
- "@backstage/app-defaults": "1.6.1",
- "example-app-next": "0.0.22",
- "app-next-example-plugin": "0.0.22",
- "example-backend": "0.0.37",
- "@backstage/backend-app-api": "1.2.2",
- "@backstage/backend-defaults": "0.9.0",
+ "example-app": "0.2.114",
+ "@backstage/app-defaults": "1.7.1",
+ "example-app-next": "0.0.28",
+ "app-next-example-plugin": "0.0.28",
+ "example-backend": "0.0.43",
+ "@backstage/backend-app-api": "1.2.8",
+ "@backstage/backend-defaults": "0.13.0",
"@backstage/backend-dev-utils": "0.1.5",
- "@backstage/backend-dynamic-feature-service": "0.6.2",
- "@backstage/backend-openapi-utils": "0.5.2",
- "@backstage/backend-plugin-api": "1.3.0",
- "@backstage/backend-test-utils": "1.4.0",
- "@backstage/canon": "0.3.0",
- "@backstage/catalog-client": "1.9.1",
- "@backstage/catalog-model": "1.7.3",
- "@backstage/cli": "0.32.0",
+ "@backstage/backend-dynamic-feature-service": "0.7.5",
+ "@backstage/backend-openapi-utils": "0.6.2",
+ "@backstage/backend-plugin-api": "1.4.4",
+ "@backstage/backend-test-utils": "1.9.1",
+ "@backstage/catalog-client": "1.12.0",
+ "@backstage/catalog-model": "1.7.5",
+ "@backstage/cli": "0.34.4",
"@backstage/cli-common": "0.1.15",
- "@backstage/cli-node": "0.2.13",
+ "@backstage/cli-node": "0.2.14",
"@backstage/codemods": "0.1.52",
- "@backstage/config": "1.3.2",
- "@backstage/config-loader": "1.10.0",
- "@backstage/core-app-api": "1.16.1",
- "@backstage/core-compat-api": "0.4.1",
- "@backstage/core-components": "0.17.1",
- "@backstage/core-plugin-api": "1.10.6",
- "@backstage/create-app": "0.6.1",
- "@backstage/dev-utils": "1.1.9",
- "e2e-test": "0.2.27",
+ "@backstage/config": "1.3.5",
+ "@backstage/config-loader": "1.10.5",
+ "@backstage/core-app-api": "1.19.1",
+ "@backstage/core-compat-api": "0.5.3",
+ "@backstage/core-components": "0.18.2",
+ "@backstage/core-plugin-api": "1.11.1",
+ "@backstage/create-app": "0.7.5",
+ "@backstage/dev-utils": "1.1.15",
+ "e2e-test": "0.2.33",
"@backstage/e2e-test-utils": "0.1.1",
"@backstage/errors": "1.2.7",
- "@backstage/eslint-plugin": "0.1.10",
- "@backstage/frontend-app-api": "0.11.1",
- "@backstage/frontend-defaults": "0.2.1",
- "@backstage/frontend-dynamic-feature-loader": "0.1.0",
- "@internal/frontend": "0.0.8",
- "@backstage/frontend-plugin-api": "0.10.1",
- "@backstage/frontend-test-utils": "0.3.1",
- "@backstage/integration": "1.16.3",
- "@backstage/integration-aws-node": "0.1.15",
- "@backstage/integration-react": "1.2.6",
+ "@backstage/eslint-plugin": "0.1.12",
+ "@backstage/frontend-app-api": "0.13.1",
+ "@backstage/frontend-defaults": "0.3.2",
+ "@backstage/frontend-dynamic-feature-loader": "0.1.6",
+ "@internal/frontend": "0.0.14",
+ "@backstage/frontend-plugin-api": "0.12.1",
+ "@backstage/frontend-test-utils": "0.4.0",
+ "@backstage/integration": "1.18.1",
+ "@backstage/integration-aws-node": "0.1.18",
+ "@backstage/integration-react": "1.2.11",
"@internal/opaque": "0.0.1",
- "@backstage/release-manifests": "0.0.12",
- "@backstage/repo-tools": "0.13.2",
- "@internal/scaffolder": "0.0.8",
- "@techdocs/cli": "1.9.2",
- "techdocs-cli-embedded-app": "0.2.107",
- "@backstage/test-utils": "1.7.7",
- "@backstage/theme": "0.6.5",
- "@backstage/types": "1.2.1",
+ "@backstage/release-manifests": "0.0.13",
+ "@backstage/repo-tools": "0.15.3",
+ "@internal/scaffolder": "0.0.14",
+ "@techdocs/cli": "1.10.0",
+ "techdocs-cli-embedded-app": "0.2.113",
+ "@backstage/test-utils": "1.7.12",
+ "@backstage/theme": "0.7.0",
+ "@backstage/types": "1.2.2",
+ "@backstage/ui": "0.8.0",
"@backstage/version-bridge": "1.0.11",
- "yarn-plugin-backstage": "0.0.4",
- "@backstage/plugin-api-docs": "0.12.6",
+ "yarn-plugin-backstage": "0.0.7",
+ "@backstage/plugin-api-docs": "0.13.0",
"@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.10",
- "@backstage/plugin-app": "0.1.8",
- "@backstage/plugin-app-backend": "0.5.1",
- "@backstage/plugin-app-node": "0.1.32",
- "@backstage/plugin-app-visualizer": "0.1.18",
- "@backstage/plugin-auth-backend": "0.24.5",
- "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.2",
- "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.2",
- "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.2",
- "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.7",
- "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.2",
- "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.2",
- "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.2",
- "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.2",
- "@backstage/plugin-auth-backend-module-github-provider": "0.3.2",
- "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.2",
- "@backstage/plugin-auth-backend-module-google-provider": "0.3.2",
- "@backstage/plugin-auth-backend-module-guest-provider": "0.2.7",
- "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.2",
- "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.2",
- "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.7",
- "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.2",
- "@backstage/plugin-auth-backend-module-okta-provider": "0.2.2",
- "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.2",
- "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.2",
- "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.2",
- "@backstage/plugin-auth-node": "0.6.2",
- "@backstage/plugin-auth-react": "0.1.14",
- "@backstage/plugin-bitbucket-cloud-common": "0.2.29",
- "@backstage/plugin-catalog": "1.29.0",
- "@backstage/plugin-catalog-backend": "1.32.1",
- "@backstage/plugin-catalog-backend-module-aws": "0.4.10",
- "@backstage/plugin-catalog-backend-module-azure": "0.3.4",
- "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.1",
- "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.4.7",
- "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.4.0",
- "@backstage/plugin-catalog-backend-module-gcp": "0.3.7",
- "@backstage/plugin-catalog-backend-module-gerrit": "0.3.1",
- "@backstage/plugin-catalog-backend-module-github": "0.8.0",
- "@backstage/plugin-catalog-backend-module-github-org": "0.3.9",
- "@backstage/plugin-catalog-backend-module-gitlab": "0.6.5",
- "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.8",
- "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.6.5",
- "@backstage/plugin-catalog-backend-module-ldap": "0.11.4",
- "@backstage/plugin-catalog-backend-module-logs": "0.1.9",
- "@backstage/plugin-catalog-backend-module-msgraph": "0.6.9",
- "@backstage/plugin-catalog-backend-module-openapi": "0.2.9",
- "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.9",
- "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.7",
- "@backstage/plugin-catalog-backend-module-unprocessed": "0.5.7",
- "@backstage/plugin-catalog-common": "1.1.3",
- "@backstage/plugin-catalog-graph": "0.4.18",
- "@backstage/plugin-catalog-import": "0.12.13",
- "@backstage/plugin-catalog-node": "1.16.3",
- "@backstage/plugin-catalog-react": "1.17.0",
- "@backstage/plugin-catalog-unprocessed-entities": "0.2.16",
- "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.7",
- "@backstage/plugin-config-schema": "0.1.67",
- "@backstage/plugin-devtools": "0.1.26",
- "@backstage/plugin-devtools-backend": "0.5.4",
- "@backstage/plugin-devtools-common": "0.1.15",
- "@backstage/plugin-events-backend": "0.5.1",
- "@backstage/plugin-events-backend-module-aws-sqs": "0.4.10",
- "@backstage/plugin-events-backend-module-azure": "0.2.19",
- "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.19",
- "@backstage/plugin-events-backend-module-bitbucket-server": "0.1.0",
- "@backstage/plugin-events-backend-module-gerrit": "0.2.19",
- "@backstage/plugin-events-backend-module-github": "0.3.0",
- "@backstage/plugin-events-backend-module-gitlab": "0.3.0",
- "@backstage/plugin-events-backend-test-utils": "0.1.43",
- "@backstage/plugin-events-node": "0.4.10",
- "@internal/plugin-todo-list": "1.0.38",
- "@internal/plugin-todo-list-backend": "1.0.38",
- "@internal/plugin-todo-list-common": "1.0.24",
- "@backstage/plugin-gateway-backend": "1.0.0",
- "@backstage/plugin-home": "0.8.7",
- "@backstage/plugin-home-react": "0.1.25",
- "@backstage/plugin-kubernetes": "0.12.6",
- "@backstage/plugin-kubernetes-backend": "0.19.5",
- "@backstage/plugin-kubernetes-cluster": "0.0.24",
- "@backstage/plugin-kubernetes-common": "0.9.4",
- "@backstage/plugin-kubernetes-node": "0.2.5",
- "@backstage/plugin-kubernetes-react": "0.5.6",
- "@backstage/plugin-notifications": "0.5.4",
- "@backstage/plugin-notifications-backend": "0.5.5",
- "@backstage/plugin-notifications-backend-module-email": "0.3.8",
- "@backstage/plugin-notifications-backend-module-slack": "0.1.0",
- "@backstage/plugin-notifications-common": "0.0.8",
- "@backstage/plugin-notifications-node": "0.2.14",
- "@backstage/plugin-org": "0.6.38",
- "@backstage/plugin-org-react": "0.1.37",
- "@backstage/plugin-permission-backend": "0.6.0",
- "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.7",
- "@backstage/plugin-permission-common": "0.8.4",
- "@backstage/plugin-permission-node": "0.9.1",
- "@backstage/plugin-permission-react": "0.4.33",
- "@backstage/plugin-proxy-backend": "0.6.1",
- "@backstage/plugin-proxy-node": "0.1.3",
- "@backstage/plugin-scaffolder": "1.30.0",
- "@backstage/plugin-scaffolder-backend": "1.32.0",
- "@backstage/plugin-scaffolder-backend-module-azure": "0.2.8",
- "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.9",
- "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.8",
- "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.8",
- "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.8",
- "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.9",
- "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.8",
- "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.8",
- "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.8",
- "@backstage/plugin-scaffolder-backend-module-github": "0.7.0",
- "@backstage/plugin-scaffolder-backend-module-gitlab": "0.9.0",
- "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.9",
- "@backstage/plugin-scaffolder-backend-module-rails": "0.5.8",
- "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.8",
- "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.9",
- "@backstage/plugin-scaffolder-common": "1.5.10",
- "@backstage/plugin-scaffolder-node": "0.8.1",
- "@backstage/plugin-scaffolder-node-test-utils": "0.2.1",
- "@backstage/plugin-scaffolder-react": "1.15.0",
- "@backstage/plugin-search": "1.4.25",
- "@backstage/plugin-search-backend": "2.0.1",
- "@backstage/plugin-search-backend-module-catalog": "0.3.3",
- "@backstage/plugin-search-backend-module-elasticsearch": "1.7.1",
- "@backstage/plugin-search-backend-module-explore": "0.3.1",
- "@backstage/plugin-search-backend-module-pg": "0.5.43",
- "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.8",
- "@backstage/plugin-search-backend-module-techdocs": "0.4.1",
- "@backstage/plugin-search-backend-node": "1.3.10",
- "@backstage/plugin-search-common": "1.2.17",
- "@backstage/plugin-search-react": "1.8.8",
- "@backstage/plugin-signals": "0.0.18",
- "@backstage/plugin-signals-backend": "0.3.3",
- "@backstage/plugin-signals-node": "0.1.19",
- "@backstage/plugin-signals-react": "0.0.12",
- "@backstage/plugin-techdocs": "1.12.5",
- "@backstage/plugin-techdocs-addons-test-utils": "1.0.47",
- "@backstage/plugin-techdocs-backend": "2.0.1",
- "@backstage/plugin-techdocs-common": "0.1.0",
- "@backstage/plugin-techdocs-module-addons-contrib": "1.1.23",
- "@backstage/plugin-techdocs-node": "1.13.2",
- "@backstage/plugin-techdocs-react": "1.2.16",
- "@backstage/plugin-user-settings": "0.8.21",
- "@backstage/plugin-user-settings-backend": "0.3.1",
- "@backstage/plugin-user-settings-common": "0.0.1",
- "@backstage/plugin-events-backend-module-google-pubsub": "0.0.0",
- "@backstage/plugin-catalog-backend-module-gitea": "0.0.0"
+ "@backstage/plugin-app": "0.3.1",
+ "@backstage/plugin-app-backend": "0.5.7",
+ "@backstage/plugin-app-node": "0.1.38",
+ "@backstage/plugin-app-visualizer": "0.1.24",
+ "@backstage/plugin-auth": "0.1.1",
+ "@backstage/plugin-auth-backend": "0.25.5",
+ "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.8",
+ "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.8",
+ "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.8",
+ "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.13",
+ "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.8",
+ "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.8",
+ "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.8",
+ "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.8",
+ "@backstage/plugin-auth-backend-module-github-provider": "0.3.8",
+ "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.8",
+ "@backstage/plugin-auth-backend-module-google-provider": "0.3.8",
+ "@backstage/plugin-auth-backend-module-guest-provider": "0.2.13",
+ "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.8",
+ "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.8",
+ "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.13",
+ "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.8",
+ "@backstage/plugin-auth-backend-module-okta-provider": "0.2.8",
+ "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.8",
+ "@backstage/plugin-auth-backend-module-openshift-provider": "0.1.1",
+ "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.8",
+ "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.8",
+ "@backstage/plugin-auth-node": "0.6.8",
+ "@backstage/plugin-auth-react": "0.1.20",
+ "@backstage/plugin-bitbucket-cloud-common": "0.3.3",
+ "@backstage/plugin-catalog": "1.31.4",
+ "@backstage/plugin-catalog-backend": "3.1.2",
+ "@backstage/plugin-catalog-backend-module-aws": "0.4.16",
+ "@backstage/plugin-catalog-backend-module-azure": "0.3.10",
+ "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.7",
+ "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.5.4",
+ "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.5.4",
+ "@backstage/plugin-catalog-backend-module-gcp": "0.3.13",
+ "@backstage/plugin-catalog-backend-module-gerrit": "0.3.7",
+ "@backstage/plugin-catalog-backend-module-gitea": "0.1.5",
+ "@backstage/plugin-catalog-backend-module-github": "0.11.1",
+ "@backstage/plugin-catalog-backend-module-github-org": "0.3.15",
+ "@backstage/plugin-catalog-backend-module-gitlab": "0.7.4",
+ "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.14",
+ "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.7.5",
+ "@backstage/plugin-catalog-backend-module-ldap": "0.11.10",
+ "@backstage/plugin-catalog-backend-module-logs": "0.1.15",
+ "@backstage/plugin-catalog-backend-module-msgraph": "0.8.1",
+ "@backstage/plugin-catalog-backend-module-openapi": "0.2.15",
+ "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.15",
+ "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.13",
+ "@backstage/plugin-catalog-backend-module-unprocessed": "0.6.5",
+ "@backstage/plugin-catalog-common": "1.1.6",
+ "@backstage/plugin-catalog-graph": "0.5.2",
+ "@backstage/plugin-catalog-import": "0.13.6",
+ "@backstage/plugin-catalog-node": "1.19.1",
+ "@backstage/plugin-catalog-react": "1.21.2",
+ "@backstage/plugin-catalog-unprocessed-entities": "0.2.22",
+ "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.10",
+ "@backstage/plugin-config-schema": "0.1.73",
+ "@backstage/plugin-devtools": "0.1.32",
+ "@backstage/plugin-devtools-backend": "0.5.10",
+ "@backstage/plugin-devtools-common": "0.1.18",
+ "@backstage/plugin-events-backend": "0.5.7",
+ "@backstage/plugin-events-backend-module-aws-sqs": "0.4.16",
+ "@backstage/plugin-events-backend-module-azure": "0.2.25",
+ "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.25",
+ "@backstage/plugin-events-backend-module-bitbucket-server": "0.1.6",
+ "@backstage/plugin-events-backend-module-gerrit": "0.2.25",
+ "@backstage/plugin-events-backend-module-github": "0.4.5",
+ "@backstage/plugin-events-backend-module-gitlab": "0.3.6",
+ "@backstage/plugin-events-backend-module-google-pubsub": "0.1.5",
+ "@backstage/plugin-events-backend-module-kafka": "0.1.4",
+ "@backstage/plugin-events-backend-test-utils": "0.1.49",
+ "@backstage/plugin-events-node": "0.4.16",
+ "@internal/plugin-todo-list": "1.0.44",
+ "@internal/plugin-todo-list-backend": "1.0.44",
+ "@internal/plugin-todo-list-common": "1.0.27",
+ "@backstage/plugin-gateway-backend": "1.0.6",
+ "@backstage/plugin-home": "0.8.13",
+ "@backstage/plugin-home-react": "0.1.31",
+ "@backstage/plugin-kubernetes": "0.12.12",
+ "@backstage/plugin-kubernetes-backend": "0.20.3",
+ "@backstage/plugin-kubernetes-cluster": "0.0.30",
+ "@backstage/plugin-kubernetes-common": "0.9.7",
+ "@backstage/plugin-kubernetes-node": "0.3.5",
+ "@backstage/plugin-kubernetes-react": "0.5.12",
+ "@backstage/plugin-mcp-actions-backend": "0.1.4",
+ "@backstage/plugin-mui-to-bui": "0.2.0",
+ "@backstage/plugin-notifications": "0.5.10",
+ "@backstage/plugin-notifications-backend": "0.5.11",
+ "@backstage/plugin-notifications-backend-module-email": "0.3.14",
+ "@backstage/plugin-notifications-backend-module-slack": "0.2.0",
+ "@backstage/plugin-notifications-common": "0.1.1",
+ "@backstage/plugin-notifications-node": "0.2.20",
+ "@backstage/plugin-org": "0.6.45",
+ "@backstage/plugin-org-react": "0.1.43",
+ "@backstage/plugin-permission-backend": "0.7.5",
+ "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.13",
+ "@backstage/plugin-permission-common": "0.9.2",
+ "@backstage/plugin-permission-node": "0.10.5",
+ "@backstage/plugin-permission-react": "0.4.37",
+ "@backstage/plugin-proxy-backend": "0.6.7",
+ "@backstage/plugin-proxy-node": "0.1.9",
+ "@backstage/plugin-scaffolder": "1.34.2",
+ "@backstage/plugin-scaffolder-backend": "3.0.0",
+ "@backstage/plugin-scaffolder-backend-module-azure": "0.2.14",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.15",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.14",
+ "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.14",
+ "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.14",
+ "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.16",
+ "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.14",
+ "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.14",
+ "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.14",
+ "@backstage/plugin-scaffolder-backend-module-github": "0.9.1",
+ "@backstage/plugin-scaffolder-backend-module-gitlab": "0.9.6",
+ "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.15",
+ "@backstage/plugin-scaffolder-backend-module-rails": "0.5.14",
+ "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.14",
+ "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.15",
+ "@backstage/plugin-scaffolder-common": "1.7.2",
+ "@backstage/plugin-scaffolder-node": "0.12.0",
+ "@backstage/plugin-scaffolder-node-test-utils": "0.3.4",
+ "@backstage/plugin-scaffolder-react": "1.19.2",
+ "@backstage/plugin-search": "1.4.31",
+ "@backstage/plugin-search-backend": "2.0.7",
+ "@backstage/plugin-search-backend-module-catalog": "0.3.9",
+ "@backstage/plugin-search-backend-module-elasticsearch": "1.7.7",
+ "@backstage/plugin-search-backend-module-explore": "0.3.8",
+ "@backstage/plugin-search-backend-module-pg": "0.5.49",
+ "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.14",
+ "@backstage/plugin-search-backend-module-techdocs": "0.4.7",
+ "@backstage/plugin-search-backend-node": "1.3.16",
+ "@backstage/plugin-search-common": "1.2.20",
+ "@backstage/plugin-search-react": "1.9.5",
+ "@backstage/plugin-signals": "0.0.24",
+ "@backstage/plugin-signals-backend": "0.3.9",
+ "@backstage/plugin-signals-node": "0.1.25",
+ "@backstage/plugin-signals-react": "0.0.16",
+ "@backstage/plugin-techdocs": "1.15.1",
+ "@backstage/plugin-techdocs-addons-test-utils": "1.1.1",
+ "@backstage/plugin-techdocs-backend": "2.1.1",
+ "@backstage/plugin-techdocs-common": "0.1.1",
+ "@backstage/plugin-techdocs-module-addons-contrib": "1.1.29",
+ "@backstage/plugin-techdocs-node": "1.13.8",
+ "@backstage/plugin-techdocs-react": "1.3.4",
+ "@backstage/plugin-user-settings": "0.8.27",
+ "@backstage/plugin-user-settings-backend": "0.3.7",
+ "@backstage/plugin-user-settings-common": "0.0.1"
},
"changesets": [
- "angry-sites-fold",
- "beige-kiwis-flow",
- "brave-donuts-sink",
- "brave-eggs-mate",
- "brave-pandas-beam",
- "brave-toes-switch",
- "breezy-hotels-deny",
- "bright-moles-sort",
- "bumpy-showers-design",
- "busy-badgers-hang",
- "calm-toys-occur",
- "chatty-months-grow",
- "chatty-showers-cheat",
- "chilly-trams-cheer",
- "chubby-cougars-run",
- "chubby-needles-vanish",
- "clean-otters-allow",
- "cold-humans-check",
- "common-goats-raise",
- "cool-bikes-push",
- "cool-cities-grab",
- "cool-colts-float",
- "cool-groups-fail",
- "cool-knives-design",
- "crazy-chefs-sin",
- "create-app-1745325336",
- "create-app-1745936753",
- "create-app-1747144256",
- "cruel-lights-sip",
- "cyan-pots-appear",
- "deep-ties-move",
- "dirty-grapes-vanish",
- "dry-carpets-hope",
- "dry-shirts-film",
- "dull-doodles-trade",
- "early-colts-accept",
- "early-dryers-teach",
- "eight-toys-feel",
- "eleven-beans-relax",
- "eleven-ghosts-strive",
- "every-bottles-grow",
- "famous-cities-stand",
- "fancy-frogs-like",
- "floppy-days-tell",
- "four-peaches-talk",
- "four-snails-argue",
- "fruity-bags-flow",
- "funny-papayas-tell",
- "good-islands-drive",
- "green-trainers-float",
- "grumpy-phones-kiss",
- "heavy-baths-rule",
- "heavy-onions-swim",
- "honest-states-repeat",
- "honest-teams-shave",
- "huge-olives-do",
- "icy-mugs-glow",
- "khaki-grapes-sink",
- "large-experts-sort",
- "large-lemons-clap",
- "lazy-tires-show",
- "lovely-cats-take",
- "mean-parents-build",
- "mighty-carrots-decide",
- "moody-women-itch",
- "neat-glasses-occur",
- "neat-glasses-occured",
- "new-hands-scream",
- "nice-vans-vanish",
- "ninety-donkeys-shop",
- "old-crews-serve",
- "old-parents-cut",
- "open-ghosts-fix",
- "open-lands-shop",
- "orange-banks-deny",
- "polite-shoes-allow",
- "pretty-corners-speak",
- "pretty-seas-hug",
- "proud-women-hammer",
- "public-socks-agree",
- "puny-rice-sneeze",
- "real-rings-smoke",
- "real-sheep-chew",
- "renovate-29506de",
- "renovate-4fc113d",
- "renovate-68baea0",
- "renovate-e32145b",
- "renovate-ee17247",
- "sad-showers-begin",
- "sad-taxes-bake",
- "shaggy-stingrays-check",
- "sharp-ligers-beg",
- "sharp-numbers-doubt",
- "shiny-symbols-grow",
- "short-teeth-juggle",
- "silent-clubs-roll",
- "slick-brooms-start",
- "slimy-peas-post",
- "slow-drinks-enjoy",
- "small-eggs-develop",
- "soft-ravens-build",
- "spicy-camels-bet",
- "spicy-steaks-swim",
- "spotty-doors-design",
- "stale-symbols-joke",
- "sweet-papayas-slide",
- "tall-suits-share-auth-backend",
- "tall-suits-share-auth-node",
- "tall-suits-share",
- "tame-areas-behave",
- "ten-spies-explode",
- "ten-tables-build",
- "thick-hotels-rhyme",
- "tidy-paths-count",
- "true-breads-rhyme",
- "true-trains-tie",
- "twenty-kiwis-punch",
- "violet-breads-obey",
- "warm-cases-bathe",
- "warm-llamas-return",
- "wicked-clubs-dream",
- "wicked-dingos-stand",
- "wise-cobras-sink",
- "wise-pillows-smile",
- "yellow-beans-eat",
- "yellow-cows-tickle",
- "young-dancers-shave"
+ "all-camels-agree",
+ "better-hats-cross",
+ "better-steaks-act",
+ "breezy-times-ring",
+ "bright-ears-send",
+ "busy-goats-create",
+ "clever-boats-clap",
+ "create-app-1761312116",
+ "cruel-items-dig",
+ "cruel-plums-talk",
+ "easy-hands-grow",
+ "eighty-results-prove",
+ "eleven-carpets-win",
+ "every-ants-count",
+ "every-clocks-arrive",
+ "fast-tools-mate",
+ "fine-hands-return",
+ "five-seas-jam",
+ "fix-text-truncate-prop",
+ "flat-paws-do",
+ "fruity-snails-laugh",
+ "funny-stars-open",
+ "gentle-bikes-relax",
+ "giant-lamps-happen",
+ "grumpy-planes-bet",
+ "heavy-cars-wash",
+ "honest-pandas-win",
+ "huge-taxis-grab",
+ "itchy-bars-smell",
+ "legal-weeks-walk",
+ "lemon-spies-sleep",
+ "long-humans-sink",
+ "loud-carpets-throw",
+ "moody-plums-add",
+ "ninety-cobras-feel",
+ "open-items-open",
+ "polite-seas-divide",
+ "pretty-kids-allow",
+ "quiet-singers-pick",
+ "rich-streets-rule",
+ "ripe-crabs-care",
+ "seven-cycles-pick",
+ "short-sides-feel",
+ "silver-garlics-thank",
+ "smart-donuts-teach",
+ "solid-bees-agree",
+ "solid-dancers-march",
+ "stupid-doodles-love",
+ "ten-houses-attack",
+ "tender-regions-know",
+ "thirty-hoops-own",
+ "tough-sloths-spend",
+ "twelve-spoons-feel",
+ "typescript-constructor-refactor",
+ "upset-teeth-add",
+ "warm-moments-repeat",
+ "warm-shrimps-clap",
+ "wide-papers-run",
+ "wild-donkeys-sneeze",
+ "wild-owls-divide",
+ "yummy-socks-brake"
]
}
diff --git a/.changeset/pretty-corners-speak.md b/.changeset/pretty-corners-speak.md
deleted file mode 100644
index 8e24d212fe..0000000000
--- a/.changeset/pretty-corners-speak.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-test-utils': minor
----
-
-Add a functional `mockServices.events()`
diff --git a/.changeset/pretty-kids-allow.md b/.changeset/pretty-kids-allow.md
new file mode 100644
index 0000000000..6b3b11a895
--- /dev/null
+++ b/.changeset/pretty-kids-allow.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-backend-module-github': patch
+---
+
+Added configurable `pageSizes` for GitHub GraphQL API queries to prevent `RESOURCE_LIMITS_EXCEEDED` errors with organizations with large number of repositories. Please see the [GitHub Discovery documentation](https://backstage.io/docs/integrations/github/discovery#configuration) for new configuration options.
diff --git a/.changeset/pretty-seas-hug.md b/.changeset/pretty-seas-hug.md
deleted file mode 100644
index 216f9af18f..0000000000
--- a/.changeset/pretty-seas-hug.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-react': patch
----
-
-Fixed an issue causing entities of kind user and group to be empty when an owner was selected
diff --git a/.changeset/proud-women-hammer.md b/.changeset/proud-women-hammer.md
deleted file mode 100644
index 030fc5bf7b..0000000000
--- a/.changeset/proud-women-hammer.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-node': patch
----
-
-trim leading and trailing slashes from parseRepoUrl query parameters
diff --git a/.changeset/public-socks-agree.md b/.changeset/public-socks-agree.md
deleted file mode 100644
index 7febb17cc0..0000000000
--- a/.changeset/public-socks-agree.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-node-test-utils': patch
----
-
-Include optional `user` in `createMockActionContext`
diff --git a/.changeset/puny-rice-sneeze.md b/.changeset/puny-rice-sneeze.md
deleted file mode 100644
index 1967b68dab..0000000000
--- a/.changeset/puny-rice-sneeze.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-home': patch
----
-
-Export root page route from the home plugin to enable adding links/nav to it from outside the plugin
diff --git a/.changeset/quiet-singers-pick.md b/.changeset/quiet-singers-pick.md
new file mode 100644
index 0000000000..cc66db1bcd
--- /dev/null
+++ b/.changeset/quiet-singers-pick.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-signals': patch
+---
+
+Fixes a bug where the `SignalClient` would try to subscribe to the same channel twice after an error, instead of just once.
diff --git a/.changeset/real-rings-smoke.md b/.changeset/real-rings-smoke.md
deleted file mode 100644
index 832d7e3c6e..0000000000
--- a/.changeset/real-rings-smoke.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-org': patch
----
-
-display entity-ref in GroupProfileCard so groups can easily determine their Group ID
diff --git a/.changeset/real-sheep-chew.md b/.changeset/real-sheep-chew.md
deleted file mode 100644
index 9ae5b5ae9e..0000000000
--- a/.changeset/real-sheep-chew.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-components': patch
----
-
-Fix the hidden sidebar's sub-menu when the sidebar is scrollable
diff --git a/.changeset/renovate-29506de.md b/.changeset/renovate-29506de.md
deleted file mode 100644
index 1260f3b453..0000000000
--- a/.changeset/renovate-29506de.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/repo-tools': patch
----
-
-Updated dependency `typedoc` to `^0.28.0`.
diff --git a/.changeset/renovate-4fc113d.md b/.changeset/renovate-4fc113d.md
deleted file mode 100644
index abec879c27..0000000000
--- a/.changeset/renovate-4fc113d.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Updated dependency `@octokit/request` to `^8.0.0`.
diff --git a/.changeset/renovate-68baea0.md b/.changeset/renovate-68baea0.md
deleted file mode 100644
index 6370fc8834..0000000000
--- a/.changeset/renovate-68baea0.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Updated dependency `react-refresh` to `^0.17.0`.
diff --git a/.changeset/renovate-e32145b.md b/.changeset/renovate-e32145b.md
deleted file mode 100644
index e1fdbcf02d..0000000000
--- a/.changeset/renovate-e32145b.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-compat-api': patch
----
-
-Updated dependency `@backstage-community/plugin-puppetdb` to `^0.6.0`.
diff --git a/.changeset/renovate-ee17247.md b/.changeset/renovate-ee17247.md
deleted file mode 100644
index a97a63d138..0000000000
--- a/.changeset/renovate-ee17247.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-'@backstage/plugin-kubernetes-backend': patch
-'@backstage/plugin-kubernetes-common': patch
-'@backstage/plugin-kubernetes-node': patch
-'@backstage/plugin-kubernetes-react': patch
-'@backstage/plugin-kubernetes': patch
----
-
-Updated dependency `@kubernetes/client-node` to `1.1.2`.
diff --git a/.changeset/rich-streets-rule.md b/.changeset/rich-streets-rule.md
new file mode 100644
index 0000000000..07f431b6a7
--- /dev/null
+++ b/.changeset/rich-streets-rule.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fix CSS layer ordering in Backstage UI to make sure component styles are loaded after tokens and base declarations.
diff --git a/.changeset/ripe-crabs-care.md b/.changeset/ripe-crabs-care.md
new file mode 100644
index 0000000000..d8cdc27652
--- /dev/null
+++ b/.changeset/ripe-crabs-care.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed RadioGroup radio button ellipse distortion by preventing flex shrink and grow.
diff --git a/.changeset/sad-showers-begin.md b/.changeset/sad-showers-begin.md
deleted file mode 100644
index 7fce23a2ab..0000000000
--- a/.changeset/sad-showers-begin.md
+++ /dev/null
@@ -1,66 +0,0 @@
----
-'@backstage/plugin-catalog-backend': major
----
-
-**BREAKING**: Removed all deprecated exports, and removed support for the old backend system.
-
-It also removes the `CodeOwnersProcessor` from the default set of processors, because it is expensive to run and has vague semantics. You need to update your backend to add it to the `catalogProcessingExtensionPoint` if you wish to continue using it.
-
-The following removed exports are available from `@backstage/plugin-catalog-node`:
-
-- `locationSpecToMetadataName`
-- `locationSpecToLocationEntity`
-- `processingResult`
-- `EntitiesSearchFilter`
-- `EntityFilter`
-- `DeferredEntity`
-- `EntityRelationSpec`
-- `CatalogProcessor`
-- `CatalogProcessorParser`
-- `CatalogProcessorCache`
-- `CatalogProcessorEmit`
-- `CatalogProcessorLocationResult`
-- `CatalogProcessorEntityResult`
-- `CatalogProcessorRelationResult`
-- `CatalogProcessorErrorResult`
-- `CatalogProcessorRefreshKeysResult`
-- `CatalogProcessorResult`
-- `EntityProvider`
-- `EntityProviderConnection`
-- `EntityProviderMutation`
-- `AnalyzeOptions`
-- `LocationAnalyzer`
-- `ScmLocationAnalyzer`
-- `PlaceholderResolver`
-- `PlaceholderResolverParams`
-- `PlaceholderResolverRead`
-- `PlaceholderResolverResolveUrl`
-- `parseEntityYaml`
-
-The following removed exports are available from `@backstage/plugin-catalog-common`:
-
-- `LocationSpec`
-- `AnalyzeLocationRequest`
-- `AnalyzeLocationResponse`
-- `AnalyzeLocationExistingEntity`
-- `AnalyzeLocationGenerateEntity`
-- `AnalyzeLocationEntityField`
-
-The following removed exports are instead implemented in the new backend system by `@backstage/plugin-search-backend-module-catalog`:
-
-- `defaultCatalogCollatorEntityTransformer`
-- `CatalogCollatorEntityTransformer`
-- `DefaultCatalogCollator`
-
-The following exports are removed without a direct replacement:
-
-- `DefaultCatalogCollatorFactory`
-- `DefaultCatalogCollatorFactoryOptions`
-- `LocationEntityProcessor`
-- `LocationEntityProcessorOptions`
-- `CatalogBuilder`
-- `CatalogEnvironment`
-- `CatalogPermissionRuleInput`
-- `CatalogProcessingEngine`
-- `createRandomProcessingInterval`
-- `ProcessingIntervalFunction`
diff --git a/.changeset/sad-taxes-bake.md b/.changeset/sad-taxes-bake.md
deleted file mode 100644
index 61a843a265..0000000000
--- a/.changeset/sad-taxes-bake.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend': patch
----
-
-Fixed bug in fs:delete that prevented wildcard patterns from matching paths starting with "."
diff --git a/.changeset/seven-cycles-pick.md b/.changeset/seven-cycles-pick.md
new file mode 100644
index 0000000000..fbd1a3895f
--- /dev/null
+++ b/.changeset/seven-cycles-pick.md
@@ -0,0 +1,17 @@
+---
+'@backstage/ui': minor
+---
+
+**BREAKING**: Changed className prop behavior to augment default styles instead of being ignored or overriding them.
+
+Affected components:
+
+- Menu, MenuListBox, MenuAutocomplete, MenuAutocompleteListbox, MenuItem, MenuListBoxItem, MenuSection, MenuSeparator
+- Switch
+- Skeleton
+- FieldLabel
+- Header, HeaderToolbar
+- HeaderPage
+- Tabs, TabList, Tab, TabPanel
+
+If you were passing custom className values to any of these components that relied on the previous behavior, you may need to adjust your styles to account for the default classes now being applied alongside your custom classes.
diff --git a/.changeset/shaggy-stingrays-check.md b/.changeset/shaggy-stingrays-check.md
deleted file mode 100644
index 74e0013224..0000000000
--- a/.changeset/shaggy-stingrays-check.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-org': patch
----
-
-Fixed missing spec.profile field on MyGroupsSidebarItem.tsx so the group spec.profile.displayName is shown on the sidebar"
diff --git a/.changeset/sharp-ligers-beg.md b/.changeset/sharp-ligers-beg.md
deleted file mode 100644
index 0582ed72d2..0000000000
--- a/.changeset/sharp-ligers-beg.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog-backend': minor
----
-
-Adds the ability to disable the default entity processors using a new boolean app config item `catalog.disableDefaultProcessors`.
diff --git a/.changeset/sharp-numbers-doubt.md b/.changeset/sharp-numbers-doubt.md
deleted file mode 100644
index 81e1313780..0000000000
--- a/.changeset/sharp-numbers-doubt.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-'@backstage/plugin-catalog-react': minor
----
-
-Added EntityOrderFilter to sort entities by different fields/columns. This new filter allows users to specify the order in which entities are displayed in the catalog.
-
-Example usage:
-
-```ts
-import {
- EntityOrderFilter,
- useEntityList,
-} from '@backstage/plugin-catalog-react';
-// ...
-const { updateFilters } = useEntityList();
-
-// ...
-updateFilters({
- order: new EntityOrderFilter([
- {
- field: 'metadata.name',
- order: 'desc',
- },
- ]),
-});
-```
diff --git a/.changeset/shiny-symbols-grow.md b/.changeset/shiny-symbols-grow.md
deleted file mode 100644
index 18dd3b8b1f..0000000000
--- a/.changeset/shiny-symbols-grow.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend': patch
-'@backstage/plugin-scaffolder-react': patch
-'@backstage/plugin-scaffolder': patch
----
-
-Fixing a bug where the name for `templatingExtensions` was incorrectly set to `templateExtensions`
diff --git a/.changeset/short-sides-feel.md b/.changeset/short-sides-feel.md
new file mode 100644
index 0000000000..a7e3542ba1
--- /dev/null
+++ b/.changeset/short-sides-feel.md
@@ -0,0 +1,5 @@
+---
+'@backstage/create-app': patch
+---
+
+Added the new `@backstage/cli/templates/catalog-provider-module` template to the explicit template configuration for the `next-app` template.
diff --git a/.changeset/short-teeth-juggle.md b/.changeset/short-teeth-juggle.md
deleted file mode 100644
index e0f1073db3..0000000000
--- a/.changeset/short-teeth-juggle.md
+++ /dev/null
@@ -1,49 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch
-'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch
-'@backstage/plugin-catalog-backend-module-backstage-openapi': patch
-'@backstage/plugin-scaffolder-backend-module-bitbucket': patch
-'@backstage/plugin-search-backend-module-elasticsearch': patch
-'@backstage/plugin-catalog-backend-module-github-org': patch
-'@backstage/plugin-scaffolder-backend-module-gerrit': patch
-'@backstage/plugin-scaffolder-backend-module-github': patch
-'@backstage/plugin-scaffolder-backend-module-gitlab': patch
-'@backstage/plugin-catalog-backend-module-msgraph': patch
-'@backstage/plugin-catalog-backend-module-gitlab': patch
-'@backstage/plugin-catalog-backend-module-logs': patch
-'@backstage/plugin-catalog-backend-module-aws': patch
-'@backstage/frontend-plugin-api': patch
-'@backstage/backend-plugin-api': patch
-'@backstage/plugin-kubernetes-backend': patch
-'@backstage/plugin-kubernetes-cluster': patch
-'@backstage/plugin-scaffolder-backend': patch
-'@backstage/backend-defaults': patch
-'@backstage/frontend-app-api': patch
-'@backstage/plugin-kubernetes-common': patch
-'@backstage/plugin-permission-common': patch
-'@backstage/backend-app-api': patch
-'@backstage/core-compat-api': patch
-'@backstage/core-components': patch
-'@backstage/plugin-kubernetes-react': patch
-'@backstage/plugin-scaffolder-react': patch
-'@backstage/plugin-techdocs-backend': patch
-'@backstage/plugin-kubernetes-node': patch
-'@backstage/plugin-scaffolder-node': patch
-'@backstage/config-loader': patch
-'@backstage/plugin-techdocs-react': patch
-'@backstage/plugin-catalog-graph': patch
-'@backstage/plugin-techdocs-node': patch
-'@backstage/plugin-auth-backend': patch
-'@backstage/repo-tools': patch
-'@backstage/plugin-auth-react': patch
-'@backstage/plugin-kubernetes': patch
-'@backstage/plugin-scaffolder': patch
-'@backstage/plugin-api-docs': patch
-'@backstage/plugin-techdocs': patch
-'@backstage/canon': patch
-'@backstage/cli': patch
-'@backstage/plugin-home': patch
-'@backstage/plugin-org': patch
----
-
-Removed various typos
diff --git a/.changeset/silent-clubs-roll.md b/.changeset/silent-clubs-roll.md
deleted file mode 100644
index c80f884331..0000000000
--- a/.changeset/silent-clubs-roll.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-permission-backend': minor
----
-
-**BREAKING** Removed support for the legacy backend system, please [migrate to the new backend system](https://backstage.io/docs/backend-system/building-backends/migrating)
diff --git a/.changeset/silver-garlics-thank.md b/.changeset/silver-garlics-thank.md
new file mode 100644
index 0000000000..d95781eb04
--- /dev/null
+++ b/.changeset/silver-garlics-thank.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fix font smoothing as default in Backstage UI.
diff --git a/.changeset/sixty-clowns-run.md b/.changeset/sixty-clowns-run.md
new file mode 100644
index 0000000000..595049df8f
--- /dev/null
+++ b/.changeset/sixty-clowns-run.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core-components': patch
+---
+
+Line numbers in LogViewer will not be selectable in UI anymore
diff --git a/.changeset/slick-brooms-start.md b/.changeset/slick-brooms-start.md
deleted file mode 100644
index dec874a867..0000000000
--- a/.changeset/slick-brooms-start.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-events-backend-module-github': patch
----
-
-Correct README installation instructions.
diff --git a/.changeset/slimy-peas-post.md b/.changeset/slimy-peas-post.md
deleted file mode 100644
index 6a0a80e323..0000000000
--- a/.changeset/slimy-peas-post.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-plugin-api': patch
----
-
-Minor doc comment update
diff --git a/.changeset/slow-drinks-enjoy.md b/.changeset/slow-drinks-enjoy.md
deleted file mode 100644
index 5a171b8fd1..0000000000
--- a/.changeset/slow-drinks-enjoy.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/integration-aws-node': patch
----
-
-Fixed bug in DefaultAwsCredentialsManager where aws.mainAccount.region has no effect on the STS region used for account ID lookup during credential provider lookup when falling back to the main account, and it does not default to us-east-1
diff --git a/.changeset/small-eggs-develop.md b/.changeset/small-eggs-develop.md
deleted file mode 100644
index 618b877fa7..0000000000
--- a/.changeset/small-eggs-develop.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/plugin-scaffolder-react': minor
-'@backstage/plugin-scaffolder': minor
----
-
-add templating extensions page
diff --git a/.changeset/smart-donuts-teach.md b/.changeset/smart-donuts-teach.md
new file mode 100644
index 0000000000..f258dee394
--- /dev/null
+++ b/.changeset/smart-donuts-teach.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Enable tree-shaking of imports other than `*.css`.
diff --git a/.changeset/soft-ravens-build.md b/.changeset/soft-ravens-build.md
deleted file mode 100644
index dfa14f9b0c..0000000000
--- a/.changeset/soft-ravens-build.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-notifications-backend-module-slack': patch
----
-
-Fix dataloader caching, and use the proper catalog service ref
diff --git a/.changeset/solid-bees-agree.md b/.changeset/solid-bees-agree.md
new file mode 100644
index 0000000000..8feecaf6d4
--- /dev/null
+++ b/.changeset/solid-bees-agree.md
@@ -0,0 +1,5 @@
+---
+'@backstage/eslint-plugin': minor
+---
+
+Added `@backstage/no-ui-css-imports-in-non-frontend` rule, which ensures that CSS from `@backstage/ui` is not imported outside of the frontend app.
diff --git a/.changeset/cold-humans-check.md b/.changeset/solid-dancers-march.md
similarity index 56%
rename from .changeset/cold-humans-check.md
rename to .changeset/solid-dancers-march.md
index 5bcb6caa4b..5633bb3e2e 100644
--- a/.changeset/cold-humans-check.md
+++ b/.changeset/solid-dancers-march.md
@@ -2,4 +2,4 @@
'@backstage/plugin-user-settings': patch
---
-plugin-user-settingsgs support i18n
+Revert `storageApiRef` implementation
diff --git a/.changeset/spicy-camels-bet.md b/.changeset/spicy-camels-bet.md
deleted file mode 100644
index 0289980ff9..0000000000
--- a/.changeset/spicy-camels-bet.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/cli': patch
----
-
-Internal refactor to move things closer to home
diff --git a/.changeset/spicy-steaks-swim.md b/.changeset/spicy-steaks-swim.md
deleted file mode 100644
index b2323c9b34..0000000000
--- a/.changeset/spicy-steaks-swim.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-github': patch
----
-
-Added optional assignees parameter to `publish:github:pull-request` action
diff --git a/.changeset/spotty-doors-design.md b/.changeset/spotty-doors-design.md
deleted file mode 100644
index 22ee61c9b0..0000000000
--- a/.changeset/spotty-doors-design.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-notifications-backend-module-slack': patch
----
-
-Added email-based Slack User ID lookup if `metadata.annotations.slack.com/bot-notify` is missing from user entity
diff --git a/.changeset/stale-symbols-joke.md b/.changeset/stale-symbols-joke.md
deleted file mode 100644
index ea4f172c1c..0000000000
--- a/.changeset/stale-symbols-joke.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-user-settings': patch
----
-
-Uppercase language name in language select
diff --git a/.changeset/stupid-doodles-love.md b/.changeset/stupid-doodles-love.md
new file mode 100644
index 0000000000..878ad853cd
--- /dev/null
+++ b/.changeset/stupid-doodles-love.md
@@ -0,0 +1,5 @@
+---
+'@techdocs/cli': patch
+---
+
+Fix for missing styles due to move to BUI.
diff --git a/.changeset/sweet-papayas-slide.md b/.changeset/sweet-papayas-slide.md
deleted file mode 100644
index 9fbc067ca7..0000000000
--- a/.changeset/sweet-papayas-slide.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-search-react': patch
----
-
-Fix memoization of `filterValue` in `SearchFilter.Autocomplete` to prevent unintended resets
diff --git a/.changeset/tall-suits-share-auth-backend.md b/.changeset/tall-suits-share-auth-backend.md
deleted file mode 100644
index 3f5230c4e3..0000000000
--- a/.changeset/tall-suits-share-auth-backend.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-auth-backend': patch
----
-
-Added support for the new `dangerousEntityRefFallback` option for `signInWithCatalogUser` in `AuthResolverContext`.
diff --git a/.changeset/tall-suits-share-auth-node.md b/.changeset/tall-suits-share-auth-node.md
deleted file mode 100644
index cccd3537f7..0000000000
--- a/.changeset/tall-suits-share-auth-node.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-'@backstage/plugin-auth-node': patch
----
-
-Added a new `dangerousEntityRefFallback` option to the `signInWithCatalogUser` method in `AuthResolverContext`. The option will cause the provided entity reference to be used as a fallback in case the user is not found in the catalog. It is up to the caller to provide the fallback entity reference.
-
-Auth providers that include pre-defined sign-in resolvers are encouraged to define a flag named `dangerouslyAllowSignInWithoutUserInCatalog` in their config, which in turn enables use of the `dangerousEntityRefFallback` option. For example:
-
-```ts
-export const usernameMatchingUserEntityName = createSignInResolverFactory({
- optionsSchema: z
- .object({
- dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(),
- })
- .optional(),
- create(options = {}) {
- return async (
- info: SignInInfo>,
- ctx,
- ) => {
- const { username } = info.result.fullProfile;
- if (!username) {
- throw new Error('User profile does not contain a username');
- }
-
- return ctx.signInWithCatalogUser(
- { entityRef: { name: username } },
- {
- dangerousEntityRefFallback:
- options?.dangerouslyAllowSignInWithoutUserInCatalog
- ? { entityRef: { name: username } }
- : undefined,
- },
- );
- };
- },
-});
-```
diff --git a/.changeset/tall-suits-share.md b/.changeset/tall-suits-share.md
deleted file mode 100644
index 05b74bc277..0000000000
--- a/.changeset/tall-suits-share.md
+++ /dev/null
@@ -1,21 +0,0 @@
----
-'@backstage/plugin-auth-backend-module-cloudflare-access-provider': patch
-'@backstage/plugin-auth-backend-module-bitbucket-server-provider': patch
-'@backstage/plugin-auth-backend-module-azure-easyauth-provider': patch
-'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': patch
-'@backstage/plugin-auth-backend-module-vmware-cloud-provider': patch
-'@backstage/plugin-auth-backend-module-atlassian-provider': patch
-'@backstage/plugin-auth-backend-module-bitbucket-provider': patch
-'@backstage/plugin-auth-backend-module-microsoft-provider': patch
-'@backstage/plugin-auth-backend-module-onelogin-provider': patch
-'@backstage/plugin-auth-backend-module-aws-alb-provider': patch
-'@backstage/plugin-auth-backend-module-gcp-iap-provider': patch
-'@backstage/plugin-auth-backend-module-github-provider': patch
-'@backstage/plugin-auth-backend-module-gitlab-provider': patch
-'@backstage/plugin-auth-backend-module-google-provider': patch
-'@backstage/plugin-auth-backend-module-oauth2-provider': patch
-'@backstage/plugin-auth-backend-module-oidc-provider': patch
-'@backstage/plugin-auth-backend-module-okta-provider': patch
----
-
-Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config.
diff --git a/.changeset/tame-areas-behave.md b/.changeset/tame-areas-behave.md
deleted file mode 100644
index 6d5c776d8b..0000000000
--- a/.changeset/tame-areas-behave.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend-module-notifications': patch
----
-
-Change notification send scaffolder action to use native zod schemas
diff --git a/.changeset/ten-houses-attack.md b/.changeset/ten-houses-attack.md
new file mode 100644
index 0000000000..9bb1ad2565
--- /dev/null
+++ b/.changeset/ten-houses-attack.md
@@ -0,0 +1,15 @@
+---
+'@backstage/plugin-scaffolder-backend-module-gitlab': patch
+'@backstage/plugin-scaffolder-backend-module-rails': patch
+'@backstage/plugin-permission-backend': patch
+'@backstage/plugin-permission-common': patch
+'@backstage/core-components': patch
+'@backstage/core-plugin-api': patch
+'@backstage/plugin-devtools-common': patch
+'@backstage/plugin-search-backend': patch
+'@backstage/core-app-api': patch
+'@backstage/plugin-catalog-graph': patch
+'@backstage/cli': patch
+---
+
+Convert all enums to erasable-syntax compliant patterns
diff --git a/.changeset/ten-spies-explode.md b/.changeset/ten-spies-explode.md
deleted file mode 100644
index 53a37fbd74..0000000000
--- a/.changeset/ten-spies-explode.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/integration': minor
----
-
-Implement Edit URL feature for Gerrit 3.9+.
-
-It's possible to disable the edit url by adding the `disableEditUrl: true` config in the Gerrit integration.
diff --git a/.changeset/ten-tables-build.md b/.changeset/ten-tables-build.md
deleted file mode 100644
index 9ddae808fa..0000000000
--- a/.changeset/ten-tables-build.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-auth-backend': patch
----
-
-The `static` key store now issues tokens with the same structure as other key stores. Tokens now include the `typ` field in the header and the `uip` (user identity proof) in the payload.
diff --git a/.changeset/tender-regions-know.md b/.changeset/tender-regions-know.md
new file mode 100644
index 0000000000..8f59567597
--- /dev/null
+++ b/.changeset/tender-regions-know.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-auth': patch
+---
+
+Authentication content screen now uses application title.
diff --git a/.changeset/thick-hotels-rhyme.md b/.changeset/thick-hotels-rhyme.md
deleted file mode 100644
index b7047dcd71..0000000000
--- a/.changeset/thick-hotels-rhyme.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-techdocs': patch
----
-
-Add hover and focus styling to the "copy to clipboard" button within codeblocks in techdocs. Also added an aria-label to the button for accessibility.
diff --git a/.changeset/thirty-hoops-own.md b/.changeset/thirty-hoops-own.md
new file mode 100644
index 0000000000..88b55d5f98
--- /dev/null
+++ b/.changeset/thirty-hoops-own.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-plugin-api': patch
+---
+
+Updated to `ExtensionInput` to make all type parameters optional.
diff --git a/.changeset/tidy-paths-count.md b/.changeset/tidy-paths-count.md
deleted file mode 100644
index 190df58043..0000000000
--- a/.changeset/tidy-paths-count.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-home': patch
----
-
-docs: Update default for `preventCollision` prop
diff --git a/.changeset/tough-sloths-spend.md b/.changeset/tough-sloths-spend.md
new file mode 100644
index 0000000000..ed6a4ab366
--- /dev/null
+++ b/.changeset/tough-sloths-spend.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-backend-module-github-org': patch
+---
+
+Added configurable `pageSizes` for GitHub GraphQL API queries to prevent `RESOURCE_LIMITS_EXCEEDED` errors with organizations with large number of teams and members. Please see the [GitHub Org Data documentation](https://backstage.io/docs/integrations/github/org#configuration-details) for new configuration options.
diff --git a/.changeset/true-breads-rhyme.md b/.changeset/true-breads-rhyme.md
deleted file mode 100644
index d54f6c18d2..0000000000
--- a/.changeset/true-breads-rhyme.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/backend-test-utils': minor
-'@backstage/backend-defaults': patch
----
-
-Added Valkey support alongside Redis in backend-defaults cache clients, using the new Keyv Valkey package. Also extended backend-test-utils to support Valkey in tests.
diff --git a/.changeset/true-trains-tie.md b/.changeset/true-trains-tie.md
deleted file mode 100644
index 678e6a85e9..0000000000
--- a/.changeset/true-trains-tie.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-notifications-backend-module-slack': patch
----
-
-Notifications which mention user entity refs are now replaced with Slack compatible mentions.
-
-Example: `Welcome <@user:default/billy>!` -> `Welcome <@U123456890>!`
diff --git a/.changeset/twelve-spoons-feel.md b/.changeset/twelve-spoons-feel.md
new file mode 100644
index 0000000000..21298b4e09
--- /dev/null
+++ b/.changeset/twelve-spoons-feel.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-catalog-backend': minor
+'@backstage/plugin-catalog-node': minor
+---
+
+Enable YAML merge keys in yamlPlaceholderResolver
diff --git a/.changeset/twenty-kiwis-punch.md b/.changeset/twenty-kiwis-punch.md
deleted file mode 100644
index 6c54760247..0000000000
--- a/.changeset/twenty-kiwis-punch.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-defaults': patch
----
-
-Bug fix: Pass user provided token through to gitlab url resolvers
diff --git a/.changeset/typescript-constructor-refactor.md b/.changeset/typescript-constructor-refactor.md
new file mode 100644
index 0000000000..b24cb5dbd0
--- /dev/null
+++ b/.changeset/typescript-constructor-refactor.md
@@ -0,0 +1,63 @@
+---
+'@backstage/plugin-catalog-backend': patch
+'@backstage/plugin-kubernetes-react': patch
+'@backstage/plugin-notifications-backend': patch
+'@backstage/plugin-notifications-node': patch
+'@backstage/plugin-permission-react': patch
+'@backstage/plugin-scaffolder-backend-module-gcp': patch
+'@backstage/plugin-scaffolder-backend': patch
+'@backstage/plugin-search-backend-module-elasticsearch': patch
+'@backstage/plugin-search-backend-module-pg': patch
+'@backstage/plugin-search-backend': patch
+'@backstage/plugin-search-backend-node': patch
+'@backstage/plugin-search-react': patch
+'@backstage/plugin-signals': patch
+'@backstage/plugin-kubernetes-backend': patch
+'@backstage/plugin-events-backend': patch
+'@backstage/plugin-catalog-backend-module-backstage-openapi': patch
+'@backstage/plugin-events-node': patch
+'@backstage/plugin-user-settings': patch
+'@backstage/plugin-auth-backend': patch
+'@backstage/plugin-auth-backend-module-cloudflare-access-provider': patch
+'@backstage/plugin-catalog-react': patch
+'@backstage/plugin-notifications-backend-module-email': patch
+'@backstage/plugin-events-backend-module-aws-sqs': patch
+'@backstage/plugin-devtools-backend': patch
+'@backstage/plugin-catalog-backend-module-unprocessed': patch
+'@backstage/plugin-catalog-backend-module-azure': patch
+'@backstage/plugin-catalog-backend-module-aws': patch
+'@backstage/plugin-scaffolder': patch
+'@backstage/plugin-techdocs-node': patch
+'@backstage/plugin-catalog-unprocessed-entities': patch
+'@backstage/plugin-catalog-backend-module-msgraph': patch
+'@backstage/plugin-catalog-backend-module-ldap': patch
+'@backstage/plugin-bitbucket-cloud-common': patch
+'@backstage/plugin-auth-node': patch
+'@backstage/backend-app-api': patch
+'@backstage/backend-defaults': patch
+'@backstage/config-loader': patch
+'@backstage/core-app-api': patch
+'@backstage/core-plugin-api': patch
+'@backstage/frontend-app-api': patch
+'@backstage/config': patch
+'@backstage/backend-dynamic-feature-service': patch
+'@backstage/core-components': patch
+'@backstage/cli': patch
+'@backstage/cli-node': patch
+'@backstage/catalog-model': patch
+'@backstage/integration': patch
+'@backstage/frontend-plugin-api': patch
+'@backstage/integration-aws-node': patch
+'@backstage/repo-tools': patch
+'@backstage/backend-test-utils': patch
+'@backstage/plugin-catalog-import': patch
+'@backstage/plugin-events-backend-module-kafka': patch
+'@backstage/plugin-kubernetes-node': patch
+'@backstage/plugin-mcp-actions-backend': patch
+'@backstage/plugin-scaffolder-node-test-utils': patch
+'@backstage/plugin-scaffolder-node': patch
+'@backstage/plugin-scaffolder-react': patch
+'@backstage/plugin-user-settings-backend': patch
+---
+
+Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility.
diff --git a/.changeset/upset-teeth-add.md b/.changeset/upset-teeth-add.md
new file mode 100644
index 0000000000..834ce7be2c
--- /dev/null
+++ b/.changeset/upset-teeth-add.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-mui-to-bui': patch
+---
+
+Updated BUI checkbox preview example to align with new component API.
diff --git a/.changeset/violet-breads-obey.md b/.changeset/violet-breads-obey.md
deleted file mode 100644
index f892e91ee2..0000000000
--- a/.changeset/violet-breads-obey.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/plugin-scaffolder': patch
----
-
-Migrate custom fields to new schema factory function;
-standardize field descriptions to prefer ui:description and present consistently,
-utilizing ScaffolderField component where possible.
diff --git a/.changeset/warm-carrots-lead.md b/.changeset/warm-carrots-lead.md
new file mode 100644
index 0000000000..fdc84d026a
--- /dev/null
+++ b/.changeset/warm-carrots-lead.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fixed Table Row component to properly support opening links in new tabs via right-click or Cmd+Click when using the href prop.
diff --git a/.changeset/warm-cases-bathe.md b/.changeset/warm-cases-bathe.md
deleted file mode 100644
index 21e9ceb098..0000000000
--- a/.changeset/warm-cases-bathe.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/backend-defaults': patch
----
-
-The `GithubUrlReader` will now use the token from `options` when fetching repo details
diff --git a/.changeset/warm-ducks-notice.md b/.changeset/warm-ducks-notice.md
new file mode 100644
index 0000000000..95e28b0836
--- /dev/null
+++ b/.changeset/warm-ducks-notice.md
@@ -0,0 +1,39 @@
+---
+'@backstage/ui': minor
+---
+
+**BREAKING**: Removed `Collapsible` component. Migrate to `Accordion` or use React Aria `Disclosure`.
+
+## Migration Path 1: Accordion (Opinionated Styled Component)
+
+Accordion provides preset styling with a similar component structure.
+
+```diff
+- import { Collapsible } from '@backstage/ui';
++ import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui';
+
+-
+- } />
+- Content
+-
+
++
++
++ Content
++
+```
+
+CSS classes: `.bui-CollapsibleRoot` → `.bui-Accordion`, `.bui-CollapsibleTrigger` → `.bui-AccordionTrigger` (now on heading element), `.bui-CollapsiblePanel` → `.bui-AccordionPanel`
+
+## Migration Path 2: React Aria Disclosure (Full Customization)
+
+For custom styling without preset styles:
+
+```tsx
+import { Disclosure, Button, DisclosurePanel } from 'react-aria-components';
+
+
+
+ Content
+;
+```
diff --git a/.changeset/warm-llamas-return.md b/.changeset/warm-llamas-return.md
deleted file mode 100644
index 743ad0a5bb..0000000000
--- a/.changeset/warm-llamas-return.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-events-backend-module-google-pubsub': minor
----
-
-Added a module that is able to transfer messages from Google Pub/Sub subscriptions into the Backstage events system.
diff --git a/.changeset/warm-moments-repeat.md b/.changeset/warm-moments-repeat.md
new file mode 100644
index 0000000000..a72387d720
--- /dev/null
+++ b/.changeset/warm-moments-repeat.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-defaults': patch
+---
+
+Fix #31348 issue where BitbucketUrlReader ignored provided token and instead always used integration credentials
diff --git a/.changeset/warm-shrimps-clap.md b/.changeset/warm-shrimps-clap.md
new file mode 100644
index 0000000000..0386df05de
--- /dev/null
+++ b/.changeset/warm-shrimps-clap.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch
+---
+
+Correctly handle entity removal computation when DB count query returns string
diff --git a/.changeset/wicked-clubs-dream.md b/.changeset/wicked-clubs-dream.md
deleted file mode 100644
index c866ac6312..0000000000
--- a/.changeset/wicked-clubs-dream.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/plugin-search-react': patch
-'@backstage/plugin-search': patch
----
-
-search plugin support i18n
diff --git a/.changeset/wicked-dingos-stand.md b/.changeset/wicked-dingos-stand.md
deleted file mode 100644
index 92d84825d1..0000000000
--- a/.changeset/wicked-dingos-stand.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/core-plugin-api': patch
----
-
-The `TranslationApi` now supports interpolation of JSX elements by passing them directly as values to the translation function. If any of the provided interpolation values are JSX elements, the translation function will return a JSX element instead of a string.
diff --git a/.changeset/wide-papers-run.md b/.changeset/wide-papers-run.md
new file mode 100644
index 0000000000..e0e16d71af
--- /dev/null
+++ b/.changeset/wide-papers-run.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Set the color-scheme property depending on theme
diff --git a/.changeset/wild-donkeys-sneeze.md b/.changeset/wild-donkeys-sneeze.md
new file mode 100644
index 0000000000..5e2c4474c5
--- /dev/null
+++ b/.changeset/wild-donkeys-sneeze.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Added new VisuallyHidden component for hiding content visually while keeping it accessible to screen readers.
diff --git a/.changeset/wild-owls-divide.md b/.changeset/wild-owls-divide.md
new file mode 100644
index 0000000000..d903b76bd0
--- /dev/null
+++ b/.changeset/wild-owls-divide.md
@@ -0,0 +1,5 @@
+---
+'@backstage/ui': patch
+---
+
+Fix default font wight and font family in Backstage UI.
diff --git a/.changeset/wise-cobras-sink.md b/.changeset/wise-cobras-sink.md
deleted file mode 100644
index 7a6a73c726..0000000000
--- a/.changeset/wise-cobras-sink.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/canon': patch
----
-
-Use the Field component from Base UI within the TextField.
diff --git a/.changeset/wise-pillows-smile.md b/.changeset/wise-pillows-smile.md
deleted file mode 100644
index febdeab2c4..0000000000
--- a/.changeset/wise-pillows-smile.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'yarn-plugin-backstage': patch
----
-
-Add both `npm:` and `backstage:` ranges to the lockfile to ensure compatibility with tools that parse the lockfile and ensure dependencies stay locked when building dist workspaces.
diff --git a/.changeset/yellow-beans-eat.md b/.changeset/yellow-beans-eat.md
deleted file mode 100644
index 2380b19a6f..0000000000
--- a/.changeset/yellow-beans-eat.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@backstage/plugin-catalog': patch
----
-
-Fix for missing `routeRef` when using `core-plugin-api` in a dialog context
diff --git a/.changeset/yellow-cows-tickle.md b/.changeset/yellow-cows-tickle.md
deleted file mode 100644
index 026c7444f0..0000000000
--- a/.changeset/yellow-cows-tickle.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@backstage/catalog-client': minor
-'@backstage/plugin-catalog-react': minor
-'@backstage/plugin-catalog-node': minor
----
-
-Add `getLocations` method to `CatalogApi` and `CatalogClient`. This method calls the [`GET /locations`](https://backstage.io/docs/features/software-catalog/software-catalog-api/#get-locations) endpoint from the catalog backend.
diff --git a/.changeset/young-dancers-shave.md b/.changeset/young-dancers-shave.md
deleted file mode 100644
index f7cdc3f3c2..0000000000
--- a/.changeset/young-dancers-shave.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@backstage/plugin-scaffolder-backend': patch
-'@backstage/plugin-scaffolder-node': patch
----
-
-Generating new tokens on each Scaffolder Task Retry
diff --git a/.changeset/yummy-socks-brake.md b/.changeset/yummy-socks-brake.md
new file mode 100644
index 0000000000..4f3dcfb598
--- /dev/null
+++ b/.changeset/yummy-socks-brake.md
@@ -0,0 +1,7 @@
+---
+'@backstage/plugin-auth-backend': patch
+---
+
+Allow configuring dynamic client registration token expiration with config `auth.experimentalDynamicClientRegistration.tokenExpiration`.
+
+Maximum expiration for the DCR token is 24 hours. Default expiration is 1 hour.
diff --git a/.eslintignore b/.eslintignore
index ea8f6760c7..5cfe8188d3 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -7,7 +7,7 @@
**/.git/**
**/public/**
**/microsite/**
-**/canon-docs/**
+**/docs-ui/**
**/templates/**
**/sample-templates/**
playwright.config.ts
diff --git a/.eslintrc.js b/.eslintrc.js
index 409b820783..54f9e69576 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -18,8 +18,26 @@ var path = require('path');
module.exports = {
root: true,
- plugins: ['@spotify', 'notice', 'react', 'testing-library'],
+ plugins: ['@spotify', 'notice', 'react', 'testing-library', '@backstage'],
rules: {
+ '@backstage/no-mixed-plugin-imports': [
+ 'error',
+ {
+ // TODO: Fix these either by right role or by moving things to new packages
+ excludedTargetPackages: [
+ '@backstage/plugin-catalog',
+ '@backstage/plugin-techdocs',
+ '@backstage/plugin-app',
+ '@backstage/plugin-catalog-backend',
+ '@backstage/test-utils',
+ '@backstage/plugin-auth-backend',
+ '@backstage/plugin-permission-backend',
+ '@backstage/plugin-kubernetes-backend',
+ '@backstage/config-loader',
+ '@backstage/plugin-app-backend'
+ ],
+ }
+ ],
'react/react-in-jsx-scope': 'off',
'notice/notice': [
'error',
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 2685926549..8491b1a260 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -10,6 +10,7 @@ yarn.lock @backstage/maintainers @backst
/.changeset @backstage/operations-maintainers
/.changeset/*.md
/.github @backstage/operations-maintainers
+/.github/vale @backstage/documentation-maintainers
/beps/0001-notifications-system @backstage/maintainers @backstage/notifications-maintainers
/docs @backstage/maintainers @backstage/documentation-maintainers
/docs/assets/search @backstage/search-maintainers
@@ -28,7 +29,6 @@ yarn.lock @backstage/maintainers @backst
/microsite/static @backstage/maintainers @backstage/documentation-maintainers
/packages @backstage/framework-maintainers
/packages/backend-openapi-utils @backstage/maintainers @backstage/reviewers @backstage/openapi-tooling-maintainers
-/packages/canon @backstage/design-system-maintainers
/packages/catalog-client @backstage/catalog-maintainers
/packages/catalog-model @backstage/catalog-maintainers
/packages/cli @backstage/tooling-maintainers
@@ -40,6 +40,7 @@ yarn.lock @backstage/maintainers @backst
/packages/repo-tools @backstage/tooling-maintainers
/packages/techdocs-cli @backstage/techdocs-maintainers
/packages/techdocs-cli-embedded-app @backstage/techdocs-maintainers
+/packages/ui @backstage/design-system-maintainers
/packages/yarn-plugin @backstage/tooling-maintainers
/plugins/app @backstage/framework-maintainers
/plugins/app-* @backstage/framework-maintainers
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 0000000000..98073c5128
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,31 @@
+This is a TypeScript project structured as a monorepo using Yarn workspaces.
+
+## Code Standards
+
+The following files contain guidelines for the project:
+
+- `/STYLE.md`: guidelines for code style.
+- `/REVIEWING.md`: guidelines for pull requests.
+- `/SECURITY.md`: guidelines for security.
+- `/docs/architecture-decisions/`: contains the architecture decisions for the project.
+
+## Development Flow
+
+Before any of these commands can be run, you need to run `yarn install` in the project root.
+
+- Build: There is no need to build the project during development, and it is verified automatically in the CI pipeline.
+- Test: Use `yarn test --no-watch ` in the project root to run tests. The path can be either a single file or a directory. Always provide a path, avoid running all tests.
+- Type checking: Use `yarn tsc` in the project root to run the type checker.
+- Code formatting: Use `yarn prettier --write ` to format code.
+- Lint: Use `yarn lint --fix` in the project root to run the linter.
+- API reports: Before submitting a pull request with changes to any package in the workspace, run `yarn build:api-reports` in the project root to generate API reports for all packages.
+
+You MUST NOT create a release by running `yarn changesets version` or `yarn release` as part of any changes. Releases are created by separate workflows.
+
+All changes that affect the published version of packages in the `/packages` and `/plugins` directories must be accompanied by a changeset. See the guidelines in `/REVIEWING.md` for information on how to write good changesets. Changesets are stored in the `/.changeset` directory. Breaking changes must be accompanied by a `minor` version bump for packages that below version `1.0.0`, or a `major` version bump for packages that are at version `1.0.0` or higher.
+
+Never update ESLint, Prettier, or TypeScript configuration files unless specifically requested.
+
+## Repository Structure
+
+See `/docs/contribute/project-structure.md` for a detailed description of the repository structure.
diff --git a/.github/renovate.json5 b/.github/renovate.json5
index 77faa9354f..da3cf3e8fb 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -31,9 +31,9 @@
matchSourceUrls: ['https://github.com/microsoft/rushstack{/,}**'],
},
{
- groupName: 'SVGR monorepo packages',
+ groupName: 'Module-federation monorepo packages',
rangeStrategy: 'replace',
- matchSourceUrls: ['https://github.com/gregberge/svgr{/,}**'],
+ matchSourceUrls: ['https://github.com/module-federation/core{/,}**'],
},
// We update yarn packages manually as it's gzip'd and we don't want to pollute the repository too much.
{
diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt
index 36f9335b1f..80d55d498a 100644
--- a/.github/vale/config/vocabularies/Backstage/accept.txt
+++ b/.github/vale/config/vocabularies/Backstage/accept.txt
@@ -10,6 +10,7 @@ Airbrakes
ajv
Alaria
Alef
+allowedHosts
allowlisted
Anddddd
Apdex
@@ -20,9 +21,9 @@ asciidoc
async
Atlassian
autofix
+autologout
automations
autoscaling
-Autoscaling
autoselect
Avro
backend's
@@ -37,15 +38,19 @@ Bitrise
Blackbox
bool
boolean
+breakpoint
Brex
broadcasted
bugfixes
builtins
bundler
bundlers
+bursty
callout
+camelCase
CDNs
Chai
+Chainguard
changeset
changesets
Changesets
@@ -75,6 +80,7 @@ Config
configmaps
configs
configurability
+configurer
conformant
const
cookiecutter
@@ -102,6 +108,9 @@ deliverables
denormalized
dependabot
deps
+dequeue
+dequeueing
+dereferencing
deserialization
destructured
destructuring
@@ -111,8 +120,6 @@ devcontainer
devcontainers
devops
devs
-dequeue
-dequeueing
dialogs
disambiguator
discoverability
@@ -143,6 +150,7 @@ Expedia
facto
failover
Fargate
+faqs
featureful
Figma
firehydrant
@@ -150,8 +158,8 @@ FireHydrant
Firekube
Firestore
Fiverr
-Flightcontrol
flightcontrol
+Flightcontrol
Francesco
Frontside
Gaurav
@@ -172,11 +180,12 @@ graphql
GraphQL
graphviz
Hackathons
+hackathon
haproxy
hardcoded
hardcoding
-Harness
harness
+Harness
Helidon
Henneke
Heroku
@@ -197,11 +206,13 @@ impactful
incentivised
Indal
indexable
+infinispan
ingestors
inlined
-inlining
inlinehilite
+inlining
integrator's
+intellij
interop
interoperate
iterable
@@ -213,11 +224,13 @@ jq
js
json
jsonnet
+JSONSchema
jsx
JWTs
Kaewkasi
Kaswell
keepalive
+Keycloak
Keyv
Knex
knip
@@ -242,6 +255,8 @@ lockdown
lockfile
lockfiles
lookbehind
+lookup
+lookups
lowercased
lunr
Luxon
@@ -273,6 +288,7 @@ modularization
monorepo
Monorepo
monorepos
+monospace
morgan
msgraph
msw
@@ -285,6 +301,7 @@ namespaced
namespaces
Namespaces
namespacing
+navbar
neuro
newrelic
nginx
@@ -312,14 +329,16 @@ OpenSearch
OpenShift
openssl
orgs
-Overridable
overridable
+Overridable
padding
paddings
pagerduty
pageview
Pandey
parallelization
+param
+params
parseable
Patrik
pattison
@@ -364,6 +383,7 @@ readonly
rebase
rebasing
Recharts
+redactions
Redash
redis
reimplement
@@ -373,6 +393,7 @@ replicasets
repo
Repo
repos
+requestors
rerender
rerenders
resourcequotas
@@ -382,8 +403,11 @@ Reusability
roadmaps
rollbar
rollout
+rollouts
Roboto
+rollbar
Rollbar
+rollout
Rollup
routable
Routable
@@ -399,9 +423,11 @@ sanitization
scaffolded
scaffolder
Scaffolder
-scrollbar
+scalprum
SCM
SCMs
+scrollable
+scrollbar
sdks
seb
semlas
@@ -409,14 +435,15 @@ semver
sendmail
serializable
Serverless
+severities
shoutout
SIG
signoff
SIGs
siloed
Sinon
-snackbars
snackbar
+snackbars
Snyk
Sonarqube
sourcemaps
@@ -433,6 +460,7 @@ statefulsets
stdout
storable
storages
+Streamable
stringified
stringify
subcommand
@@ -444,7 +472,10 @@ subfolders
subheader
subheaders
subkey
+subpage
subpath
+subpaths
+subroute
subroutes
substring
subtree
@@ -453,6 +484,7 @@ Superfences
superset
supertype
SVGs
+swappable
talkdesk
Talkdesk
Tanzu
@@ -468,6 +500,7 @@ templaters
Templaters
TFRecord
theia
+Themer
threenager
thumbsup
todo
@@ -486,6 +519,7 @@ transpiled
transpiler
transpilers
truthy
+tsconfig
TSDoc
typeahead
ui
@@ -536,7 +570,5 @@ zod
Zolotusky
zoomable
zsh
-scrollable
-severities
-intellij
-tsconfig
+resizable
+enums
diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml
index af625f67ab..29b8545766 100644
--- a/.github/workflows/api-breaking-changes-comment.yml
+++ b/.github/workflows/api-breaking-changes-comment.yml
@@ -23,7 +23,7 @@ jobs:
comment-cache-key: ${{ steps.hash.outputs.COMMENT_FILE_HASH }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
disable-sudo: true
egress-policy: block
@@ -74,7 +74,7 @@ jobs:
- name: Cache Comment
if: ${{ steps.event.outputs.ACTION != 'closed' }}
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: comment.md
key: ${{ steps.hash.outputs.COMMENT_FILE_HASH }}
@@ -99,11 +99,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
- name: Fetch cached Manifests File
id: cache
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: comment.md
key: ${{ needs.setup.outputs.comment-cache-key }}
diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml
index 74473f30d5..5ee0e35af9 100644
--- a/.github/workflows/api-breaking-changes.yml
+++ b/.github/workflows/api-breaking-changes.yml
@@ -14,7 +14,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/automate_area-labels.yml b/.github/workflows/automate_area-labels.yml
index ec6dd0c85c..0620c877eb 100644
--- a/.github/workflows/automate_area-labels.yml
+++ b/.github/workflows/automate_area-labels.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml
index c1245c59b9..5c91ff0038 100644
--- a/.github/workflows/automate_changeset_feedback.yml
+++ b/.github/workflows/automate_changeset_feedback.yml
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml
index ebe2ef739d..2476ab71f4 100644
--- a/.github/workflows/automate_merge_message.yml
+++ b/.github/workflows/automate_merge_message.yml
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml
index 6be74ae99f..56bf1e26ec 100644
--- a/.github/workflows/automate_stale.yml
+++ b/.github/workflows/automate_stale.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -27,7 +27,7 @@ jobs:
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
days-before-issue-stale: 60
- days-before-issue-close: 7
+ days-before-issue-close: 14
exempt-issue-labels: after vacations,no stale
stale-issue-label: stale
stale-pr-message: >
@@ -35,8 +35,8 @@ jobs:
recent activity from the author. It will be closed if no further activity occurs.
If the PR was closed and you want it re-opened, let us know
and we'll re-open the PR so that you can continue the contribution!
- days-before-pr-stale: 14
- days-before-pr-close: 7
+ days-before-pr-stale: 30
+ days-before-pr-close: 14
exempt-pr-labels: after vacations,no stale
stale-pr-label: stale
operations-per-run: 100
diff --git a/.github/workflows/ci-noop.yml b/.github/workflows/ci-noop.yml
index 472a7a4e72..b1415652e5 100644
--- a/.github/workflows/ci-noop.yml
+++ b/.github/workflows/ci-noop.yml
@@ -24,7 +24,7 @@ jobs:
name: Verify ${{ matrix.node-version }}
steps:
# - name: Harden Runner
- # uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # v2.5.1
+ # uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.5.1
# with:
# egress-policy: audit
@@ -40,7 +40,7 @@ jobs:
name: Test ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 059596265e..9eafd2c2a8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,7 +28,7 @@ jobs:
name: Install ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -64,7 +64,7 @@ jobs:
name: Verify ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -102,9 +102,6 @@ jobs:
- name: type checking and declarations
run: yarn tsc:full
- - name: prettier
- run: yarn prettier:check
-
# We need to generate the API references as well, so that we can verify the doc links
- name: check api reports and generate API reference
run: yarn build:api-reports:only --ci --docs
@@ -127,9 +124,9 @@ jobs:
- name: build all packages
run: yarn backstage-cli repo build --all
- # For now canon has a custom build script and needs to be built separately
- - name: build canon
- run: yarn --cwd packages/canon build
+ # For now BUI has a custom build script and needs to be built separately
+ - name: build BUI
+ run: yarn --cwd packages/ui build
- name: verify type dependencies
run: yarn lint:type-deps
@@ -161,8 +158,8 @@ jobs:
name: Test ${{ matrix.node-version }}
services:
- postgres17:
- image: postgres:17
+ postgres18:
+ image: postgres:18
env:
POSTGRES_PASSWORD: postgres
options: >-
@@ -172,8 +169,8 @@ jobs:
--health-retries 5
ports:
- 5432/tcp
- postgres13:
- image: postgres:13
+ postgres14:
+ image: postgres:14
env:
POSTGRES_PASSWORD: postgres
options: >-
@@ -239,13 +236,16 @@ jobs:
# Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
- name: restore backstage-cli cache
- uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .cache/backstage-cli
key: ${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-
+ - name: prettier
+ run: yarn prettier:check --cache --cache-location .cache/backstage-cli/.prettier-cache
+
- name: lint changed packages
run: yarn backstage-cli repo lint --since origin/master --successCache --successCacheDir .cache/backstage-cli
@@ -253,14 +253,14 @@ jobs:
run: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --since origin/master --successCache --successCacheDir .cache/backstage-cli
env:
BACKSTAGE_TEST_DISABLE_DOCKER: 1
- BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres17.ports[5432] }}
- BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }}
+ BACKSTAGE_TEST_DATABASE_POSTGRES18_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres18.ports[5432] }}
+ BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres14.ports[5432] }}
BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored
BACKSTAGE_TEST_CACHE_REDIS7_CONNECTION_STRING: redis://localhost:${{ job.services.redis.ports[6379] }}
# Always save success cache even if there were failures, that way it can be used in re-triggered builds
- name: save backstage-cli cache
- uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
if: always()
with:
path: .cache/backstage-cli
diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml
index b48bd2683d..156d676cd2 100644
--- a/.github/workflows/cron.yml
+++ b/.github/workflows/cron.yml
@@ -10,7 +10,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml
index e1547801ef..18987aa3e0 100644
--- a/.github/workflows/deploy_docker-image.yml
+++ b/.github/workflows/deploy_docker-image.yml
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -52,17 +52,17 @@ jobs:
working-directory: ./example-app
- name: Login to GitHub Container Registry
- uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
+ uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
+ uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Build and push
- uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
+ uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: './example-app'
file: ./example-app/packages/backend/Dockerfile
diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml
index 5045e0c96f..a07ac06d99 100644
--- a/.github/workflows/deploy_microsite.yml
+++ b/.github/workflows/deploy_microsite.yml
@@ -23,12 +23,12 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
- name: find latest release
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
id: find-release
with:
script: |
@@ -80,6 +80,34 @@ jobs:
if-no-files-found: error
retention-days: 1
+ # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
+ - name: restore package-docs cache
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ with:
+ path: .cache/package-docs
+ key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-
+
+ - name: build API reference (beta)
+ run: yarn backstage-repo-tools package-docs
+
+ - name: upload API reference (beta)
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: stable-reference-beta
+ path: type-docs/
+ if-no-files-found: error
+ retention-days: 1
+
+ # Always save success cache even if there were failures, that way it can be used in re-triggered builds
+ - name: save package-docs cache
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ if: always()
+ with:
+ path: .cache/package-docs
+ key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-${{ github.run_id }}
+
- name: microsite yarn install
run: yarn install --immutable
working-directory: microsite
@@ -107,7 +135,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -136,20 +164,42 @@ jobs:
if-no-files-found: error
retention-days: 1
- # Also build and upload storybook
- - name: storybook yarn install
- run: yarn install --immutable
- working-directory: storybook
+ # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
+ - name: restore package-docs cache
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ with:
+ path: .cache/package-docs
+ key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-
- - name: storybook build
+ - name: build API reference (beta)
+ run: yarn backstage-repo-tools package-docs
+
+ - name: upload API reference (beta)
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: next-reference-beta
+ path: type-docs/
+ if-no-files-found: error
+ retention-days: 1
+
+ # Always save success cache even if there were failures, that way it can be used in re-triggered builds
+ - name: save package-docs cache
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ if: always()
+ with:
+ path: .cache/package-docs
+ key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-${{ github.run_id }}
+
+ - name: Build Storybook
run: yarn build-storybook
- working-directory: storybook
- - name: storybook upload
+ - name: Upload Storybook
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: storybook
- path: storybook/dist/
+ path: dist-storybook
if-no-files-found: error
retention-days: 1
@@ -190,7 +240,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -265,8 +315,18 @@ jobs:
name: storybook
path: microsite/build/storybook
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
+ with:
+ name: stable-reference-beta
+ path: microsite/build/api/stable/
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
+ with:
+ name: next-reference-beta
+ path: microsite/build/api/next/
+
- name: Check the build output
- run: ls microsite/build && ls microsite/build/storybook
+ run: ls microsite/build && ls microsite/build/storybook && ls microsite/build/api/stable && ls microsite/build/api/next
- name: Deploy both microsite and storybook to gh-pages
uses: JamesIves/github-pages-deploy-action@6c2d9db40f9296374acc17b90404b6e8864128c8 # v4.7.3
diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml
index b1d79713a4..b968c51782 100644
--- a/.github/workflows/deploy_packages.yml
+++ b/.github/workflows/deploy_packages.yml
@@ -16,8 +16,8 @@ jobs:
node-version: [20.x, 22.x]
services:
- postgres17:
- image: postgres:17
+ postgres18:
+ image: postgres:18
env:
POSTGRES_PASSWORD: postgres
options: >-
@@ -27,8 +27,8 @@ jobs:
--health-retries 5
ports:
- 5432/tcp
- postgres13:
- image: postgres:13
+ postgres14:
+ image: postgres:14
env:
POSTGRES_PASSWORD: postgres
options: >-
@@ -94,7 +94,7 @@ jobs:
run: yarn backstage-cli config:check --lax
- name: backstage-cli cache
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .cache/backstage-cli
key: ${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-${{ github.run_id }}
@@ -110,9 +110,9 @@ jobs:
- name: build
run: yarn backstage-cli repo build --all
- # For now canon has a custom build script and needs to be built separately
- - name: build canon
- run: yarn --cwd packages/canon build
+ # For now BUI has a custom build script and needs to be built separately
+ - name: build BUI
+ run: yarn --cwd packages/ui build
- name: verify type dependencies
run: yarn lint:type-deps
@@ -122,14 +122,14 @@ jobs:
yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --coverage --successCache --successCacheDir .cache/backstage-cli
env:
BACKSTAGE_TEST_DISABLE_DOCKER: 1
- BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres17.ports[5432] }}
- BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }}
+ BACKSTAGE_TEST_DATABASE_postgres18_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres18.ports[5432] }}
+ BACKSTAGE_TEST_DATABASE_postgres14_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres14.ports[5432] }}
BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored
BACKSTAGE_TEST_CACHE_REDIS7_CONNECTION_STRING: redis://localhost:${{ job.services.redis.ports[6379] }}
- name: Discord notification
if: ${{ failure() }}
- uses: Ilshidur/action-discord@0c4b27844ba47cb1c7bee539c8eead5284ce9fa9 # 0.3.2
+ uses: Ilshidur/action-discord@d2594079a10f1d6739ee50a2471f0ca57418b554 # 0.4.0
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
with:
@@ -151,13 +151,13 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
# Notify maintainers that a new release is ready to be published
- name: Discord notification
- uses: Ilshidur/action-discord@0c4b27844ba47cb1c7bee539c8eead5284ce9fa9 # 0.3.2
+ uses: Ilshidur/action-discord@d2594079a10f1d6739ee50a2471f0ca57418b554 # 0.4.0
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_MAINTAINERS_WEBHOOK }}
with:
diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml
index 50d0e4ceb0..8c1b0d028b 100644
--- a/.github/workflows/issue.yaml
+++ b/.github/workflows/issue.yaml
@@ -16,13 +16,13 @@ jobs:
if: github.repository == 'backstage/backstage'
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
# We need to checkout the `.github/ISSUE_TEMPLATE` for the advanced labeler action to be able to read the templates
# While at it we might as well checkout all of `.github` so that the labeling actions don't need to fetch their configs
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
with:
sparse-checkout: .github
diff --git a/.github/workflows/mui-migration-tracker.yml b/.github/workflows/mui-migration-tracker.yml
new file mode 100644
index 0000000000..4754ec51b0
--- /dev/null
+++ b/.github/workflows/mui-migration-tracker.yml
@@ -0,0 +1,70 @@
+name: MUI to BUI Migration Tracker
+
+on:
+ schedule:
+ # Run daily at midnight UTC
+ - cron: '0 0 * * *'
+ workflow_dispatch:
+ # Allow manual triggering
+
+permissions:
+ issues: write
+ contents: read
+
+jobs:
+ update-migration-progress:
+ runs-on: ubuntu-latest
+ name: Update Migration Progress Issue
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version: 20.x
+ registry-url: https://registry.npmjs.org/ # Needed for auth
+
+ - name: yarn install
+ uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
+ with:
+ cache-prefix: ${{ runner.os }}-v20.x
+
+ - name: Run migration analysis
+ id: analysis
+ run: |
+ # Run the migration script and save markdown output
+ yarn mui-to-bui --markdown > migration-report.md
+
+ # Read the report into an environment variable (escape for GitHub Actions)
+ echo "REPORT<> $GITHUB_ENV
+ cat migration-report.md >> $GITHUB_ENV
+ echo "EOF" >> $GITHUB_ENV
+
+ - name: Update GitHub Issue
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const issueNumber = 31467;
+ const reportBody = process.env.REPORT;
+
+ try {
+ await github.rest.issues.update({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issueNumber,
+ body: reportBody
+ });
+
+ console.log(`✅ Successfully updated issue #${issueNumber}`);
+ } catch (error) {
+ console.error(`❌ Error updating issue: ${error.message}`);
+ throw error;
+ }
diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml
index e2bdb4c1cb..dc90da1384 100644
--- a/.github/workflows/pr-review-comment-trigger.yaml
+++ b/.github/workflows/pr-review-comment-trigger.yaml
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/pr-review-comment.yaml b/.github/workflows/pr-review-comment.yaml
index e108f3a4d7..30e7f5bad4 100644
--- a/.github/workflows/pr-review-comment.yaml
+++ b/.github/workflows/pr-review-comment.yaml
@@ -17,7 +17,7 @@ jobs:
steps:
# Inspired by https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml
index ab2630c2d9..b1a67bec15 100644
--- a/.github/workflows/pr.yaml
+++ b/.github/workflows/pr.yaml
@@ -18,7 +18,7 @@ jobs:
if: github.repository == 'backstage/backstage' && ( github.event.pull_request || github.event.issue.pull_request )
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 45eb5de5c8..5bb4c13b25 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -29,7 +29,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -39,7 +39,7 @@ jobs:
persist-credentials: false
- name: 'Run analysis'
- uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
+ uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
- uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
+ uses: github/codeql-action/upload-sarif@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
with:
sarif_file: results.sarif
diff --git a/.github/workflows/sync_canon.yml b/.github/workflows/sync_canon.yml
index 2177177df0..4868fdaca9 100644
--- a/.github/workflows/sync_canon.yml
+++ b/.github/workflows/sync_canon.yml
@@ -1,14 +1,14 @@
-name: Sync Canon Docs
+name: Sync BUI Docs
on:
push:
branches: [master]
jobs:
- sync-canon-docs:
+ sync-docs-ui:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -26,36 +26,36 @@ jobs:
with:
cache-prefix: ${{ runner.os }}-v20.x
- - name: Checkout backstage/canon-docs
+ - name: Checkout backstage/docs-ui
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
- repository: backstage/canon-docs
- path: canon-external-docs
+ repository: backstage/docs-ui
+ path: bui-external-docs
token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
- name: Configure Git
run: |
git config --global user.email noreply@backstage.io
- git config --global user.name 'Github Canon Docs workflow'
+ git config --global user.name 'Github BUI Docs workflow'
- name: Install dependencies
- working-directory: canon-docs
+ working-directory: docs-ui
run: yarn install
- - name: Build Canon Docs
- working-directory: canon-docs
+ - name: Build Docs UI
+ working-directory: docs-ui
run: |
yarn build
- - name: Replace contents of canon-external-docs repo with docs build output
- working-directory: canon-external-docs
+ - name: Replace contents of bui-external-docs repo with docs build output
+ working-directory: bui-external-docs
run: |
git rm -rf .
- cp -R ../canon-docs/dist/. .
+ cp -R ../docs-ui/dist/. .
- - name: Commit to canon-storybook repo
- working-directory: canon-external-docs
+ - name: Commit to bui-storybook repo
+ working-directory: bui-external-docs
run: |
git add .
- git commit -am "Canon Docs build for backstage/backstage@${{ github.sha }}"
+ git commit -am "BUI Docs build for backstage/backstage@${{ github.sha }}"
git push
diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml
index ee97f3fef3..458fa9ed99 100644
--- a/.github/workflows/sync_code-formatting.yml
+++ b/.github/workflows/sync_code-formatting.yml
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -30,7 +30,7 @@ jobs:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- name: Run Prettier on ADOPTERS.md
- uses: creyD/prettier_action@31355f8eef017f8aeba2e0bc09d8502b13dbbad1 # v4.3
+ uses: creyD/prettier_action@8c18391fdc98ed0d884c6345f03975edac71b8f0 # v4.6
with:
# Modifies commit only if prettier autofixed the ADOPTERS.md
prettier_options: --config docs/prettier.config.js --write ADOPTERS.md
diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml
index 42efaa1142..26c0387cce 100644
--- a/.github/workflows/sync_dependabot-changesets.yml
+++ b/.github/workflows/sync_dependabot-changesets.yml
@@ -11,7 +11,7 @@ jobs:
if: github.actor == 'dependabot[bot]' && github.repository == 'backstage/backstage'
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml
index 1c9d37aef0..7cf7e018bd 100644
--- a/.github/workflows/sync_release-manifest.yml
+++ b/.github/workflows/sync_release-manifest.yml
@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml
index 2eb8f13dbc..caf697714a 100644
--- a/.github/workflows/sync_renovate-changesets.yml
+++ b/.github/workflows/sync_renovate-changesets.yml
@@ -11,7 +11,7 @@ jobs:
if: github.actor == 'renovate[bot]' && github.repository == 'backstage/backstage'
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml
index b7cc0b33c7..697f63c991 100644
--- a/.github/workflows/sync_snyk-github-issues.yml
+++ b/.github/workflows/sync_snyk-github-issues.yml
@@ -12,7 +12,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -29,7 +29,7 @@ jobs:
cache-prefix: ${{ runner.os }}-v20.x
- name: Create Snyk report
- uses: snyk/actions/node@cdb760004ba9ea4d525f2e043745dfe85bb9077e # master
+ uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master
continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found
with:
args: >
diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml
index 741471d558..3f12815379 100644
--- a/.github/workflows/sync_snyk-monitor.yml
+++ b/.github/workflows/sync_snyk-monitor.yml
@@ -25,13 +25,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Monitor and Synchronize Snyk Policies
- uses: snyk/actions/node@cdb760004ba9ea4d525f2e043745dfe85bb9077e # master
+ uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master
with:
command: monitor
args: >
@@ -46,7 +46,7 @@ jobs:
# Above we run the `monitor` command, this runs the `test` command which is
# the one that generates the SARIF report that we can upload to GitHub.
- name: Create Snyk report
- uses: snyk/actions/node@cdb760004ba9ea4d525f2e043745dfe85bb9077e # master
+ uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master
continue-on-error: true # To make sure that SARIF upload gets called
with:
args: >
@@ -58,6 +58,6 @@ jobs:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
NODE_OPTIONS: --max-old-space-size=7168
- name: Upload Snyk report
- uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
+ uses: github/codeql-action/upload-sarif@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
with:
sarif_file: snyk.sarif
diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml
index 42dadb5a57..3071990fbd 100644
--- a/.github/workflows/sync_version-packages.yml
+++ b/.github/workflows/sync_version-packages.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -37,7 +37,7 @@ jobs:
- name: Discord notification
if: ${{ failure() }}
- uses: Ilshidur/action-discord@0c4b27844ba47cb1c7bee539c8eead5284ce9fa9 # 0.3.2
+ uses: Ilshidur/action-discord@d2594079a10f1d6739ee50a2471f0ca57418b554 # 0.4.0
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
with:
diff --git a/.github/workflows/verify_accessibility-noop.yml b/.github/workflows/verify_accessibility-noop.yml
index c33acfd86d..837e8153be 100644
--- a/.github/workflows/verify_accessibility-noop.yml
+++ b/.github/workflows/verify_accessibility-noop.yml
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml
index 02a303f9ea..c64a4d2ed1 100644
--- a/.github/workflows/verify_accessibility.yml
+++ b/.github/workflows/verify_accessibility.yml
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_chromatic-noop.yml b/.github/workflows/verify_chromatic-noop.yml
new file mode 100644
index 0000000000..cd6248e004
--- /dev/null
+++ b/.github/workflows/verify_chromatic-noop.yml
@@ -0,0 +1,27 @@
+# NO-OP placeholder that always passes for other paths
+# This is here so that we're able to set the status check as required
+
+name: Chromatic Void
+on:
+ pull_request:
+ paths-ignore:
+ - '.github/workflows/verify_chromatic.yml'
+ - '.storybook/**'
+ - 'packages/ui/src/**'
+ - '**/*.stories.tsx'
+
+permissions:
+ contents: read
+
+jobs:
+ noop:
+ runs-on: ubuntu-latest
+
+ name: Chromatic
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
+ with:
+ egress-policy: audit
+
+ - run: echo NOOP
diff --git a/.github/workflows/verify_chromatic.yml b/.github/workflows/verify_chromatic.yml
new file mode 100644
index 0000000000..705d769c83
--- /dev/null
+++ b/.github/workflows/verify_chromatic.yml
@@ -0,0 +1,92 @@
+name: Chromatic
+on:
+ # NOTE: If you change these you must update verify_storybook-noop.yml as well
+ workflow_dispatch:
+ push:
+ branches:
+ - master
+ pull_request:
+ paths:
+ - '.github/workflows/verify_chromatic.yml'
+ - '.storybook/**'
+ - 'packages/ui/**'
+ - '**/*.stories.tsx'
+
+jobs:
+ chromatic:
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ node-version: [20.x]
+
+ name: Chromatic
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout code
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ fetch-depth: 10000 # Required to retrieve git history
+
+ - name: Use node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version: ${{ matrix.node-version }}
+ registry-url: https://registry.npmjs.org/ # Needed for auth
+
+ - name: Install dependencies
+ uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
+ with:
+ cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+
+ - name: Build Storybook
+ run: yarn build-storybook:chromatic
+
+ - name: Run Chromatic
+ id: chromatic
+ uses: chromaui/action@latest
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ # projectToken intentionally shared to allow collaborators to run Chromatic on forks
+ # https://www.chromatic.com/docs/custom-ci-provider#run-chromatic-on-external-forks-of-open-source-projects
+ projectToken: chpt_dab72dc0f97d55b
+ storybookBuildDir: dist-storybook
+ onlyChanged: true
+ externals: |
+ packages/ui/**/*.css
+ packages/ui/css/**
+ packages/ui/src/**/*.css
+
+ - name: Prepare Chromatic Message
+ if: github.event_name == 'pull_request' && steps.chromatic.outputs.url
+ id: prepareMessage
+ run: |
+ if [ "${{ steps.chromatic.outputs.changeCount }}" = "0" ] || [ -z "${{ steps.chromatic.outputs.changeCount }}" ]; then
+ echo "changesText=No visual changes detected - [**View build in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT
+ else
+ echo "changesText=**${{ steps.chromatic.outputs.changeCount }}** visual changes are waiting for review - [**Review changes in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Post Chromatic Results to Job Summary
+ if: github.event_name == 'pull_request' && steps.chromatic.outputs.url
+ run: |
+ echo "## 🎨 Visual Testing with Chromatic" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- ${{ steps.prepareMessage.outputs.changesText }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **${{ steps.chromatic.outputs.specCount}}** stories for **${{ steps.chromatic.outputs.componentCount}}** Components - [**Preview changes in Storybook**](${{ steps.chromatic.outputs.storybookUrl }})" >> $GITHUB_STEP_SUMMARY
+
+ - name: Post Chromatic Link in PR Comment
+ if: github.event_name == 'pull_request' && steps.chromatic.outputs.url && github.event.pull_request.head.repo.full_name == github.repository
+ uses: mshick/add-pr-comment@v2
+ with:
+ message: |
+ ## 🎨 Visual Testing with Chromatic
+
+ - ${{ steps.prepareMessage.outputs.changesText }}
+ - **${{ steps.chromatic.outputs.specCount}}** stories for **${{ steps.chromatic.outputs.componentCount}}** Components - [**Preview changes in Storybook**](${{ steps.chromatic.outputs.storybookUrl }})
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml
index c4b21a9e1f..57da6f7f21 100644
--- a/.github/workflows/verify_codeql.yml
+++ b/.github/workflows/verify_codeql.yml
@@ -42,7 +42,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -55,7 +55,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
+ uses: github/codeql-action/init@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -66,7 +66,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
+ uses: github/codeql-action/autobuild@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -80,4 +80,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
+ uses: github/codeql-action/analyze@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6
diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml
index a3b80f498a..a0ec5a2eee 100644
--- a/.github/workflows/verify_docs-quality.yml
+++ b/.github/workflows/verify_docs-quality.yml
@@ -12,7 +12,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_e2e-linux-noop.yml b/.github/workflows/verify_e2e-linux-noop.yml
index 65fc6eccc9..f9e3d26e89 100644
--- a/.github/workflows/verify_e2e-linux-noop.yml
+++ b/.github/workflows/verify_e2e-linux-noop.yml
@@ -29,7 +29,7 @@ jobs:
name: E2E Linux ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml
index 0995a85216..898de3d57c 100644
--- a/.github/workflows/verify_e2e-linux.yml
+++ b/.github/workflows/verify_e2e-linux.yml
@@ -21,7 +21,7 @@ jobs:
services:
postgres:
- image: postgres:13
+ image: postgres:14
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -43,7 +43,7 @@ jobs:
name: E2E Linux ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml
index fc30b377bc..abfdf17ee6 100644
--- a/.github/workflows/verify_e2e-techdocs.yml
+++ b/.github/workflows/verify_e2e-techdocs.yml
@@ -32,7 +32,7 @@ jobs:
name: Techdocs
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -41,11 +41,14 @@ jobs:
with:
python-version: '3.9'
- - name: install dependencies
- run: yarn install --immutable
-
- - name: generate types
- run: yarn tsc
+ - name: use node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: yarn install
+ uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
+ with:
+ cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- name: build techdocs-cli
working-directory: packages/techdocs-cli
diff --git a/.github/workflows/verify_e2e-windows-noop.yml b/.github/workflows/verify_e2e-windows-noop.yml
index e713f33ed6..fc4af922a5 100644
--- a/.github/workflows/verify_e2e-windows-noop.yml
+++ b/.github/workflows/verify_e2e-windows-noop.yml
@@ -25,7 +25,7 @@ jobs:
name: E2E Windows ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml
index 0dc0bdaade..e859468462 100644
--- a/.github/workflows/verify_e2e-windows.yml
+++ b/.github/workflows/verify_e2e-windows.yml
@@ -33,7 +33,7 @@ jobs:
name: E2E Windows ${{ matrix.node-version }}
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml
index 7bc612bf64..8f09531a94 100644
--- a/.github/workflows/verify_fossa.yml
+++ b/.github/workflows/verify_fossa.yml
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_microsite-noop.yml b/.github/workflows/verify_microsite-noop.yml
index da4bc6ba25..169e447bbb 100644
--- a/.github/workflows/verify_microsite-noop.yml
+++ b/.github/workflows/verify_microsite-noop.yml
@@ -21,7 +21,7 @@ jobs:
name: Microsite
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml
index c22648bb8c..60fa039ec4 100644
--- a/.github/workflows/verify_microsite.yml
+++ b/.github/workflows/verify_microsite.yml
@@ -28,12 +28,12 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
- name: find latest release
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
id: find-release
with:
script: |
@@ -85,6 +85,32 @@ jobs:
if-no-files-found: error
retention-days: 1
+ # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
+ - name: restore package-docs cache
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ with:
+ path: .cache/package-docs
+ key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-
+
+ - name: build API reference (beta)
+ run: yarn backstage-repo-tools package-docs
+
+ - name: upload API reference (beta)
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: stable-reference-beta
+ path: type-docs/
+
+ # Always save success cache even if there were failures, that way it can be used in re-triggered builds
+ - name: save package-docs cache
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ if: always()
+ with:
+ path: .cache/package-docs
+ key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-${{ github.run_id }}
+
- name: microsite yarn install
run: yarn install --immutable
working-directory: microsite
@@ -111,7 +137,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -140,20 +166,40 @@ jobs:
if-no-files-found: error
retention-days: 1
- # Also build and upload storybook
- - name: storybook yarn install
- run: yarn install --immutable
- working-directory: storybook
+ # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds
+ - name: restore package-docs cache
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ with:
+ path: .cache/package-docs
+ key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-next-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-next-
- - name: storybook build
+ - name: build API reference (beta)
+ run: yarn backstage-repo-tools package-docs
+
+ - name: upload API reference (beta)
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: next-reference-beta
+ path: type-docs/
+
+ # Always save success cache even if there were failures, that way it can be used in re-triggered builds
+ - name: save package-docs cache
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ if: always()
+ with:
+ path: .cache/package-docs
+ key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-next-${{ github.run_id }}
+
+ - name: Build Storybook
run: yarn build-storybook
- working-directory: storybook
- - name: storybook upload
+ - name: Upload Storybook
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: storybook
- path: storybook/dist/
+ path: dist-storybook
if-no-files-found: error
retention-days: 1
@@ -188,7 +234,7 @@ jobs:
name: Microsite
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
@@ -271,3 +317,15 @@ jobs:
- name: build microsite
run: yarn build
working-directory: microsite
+
+ - name: download stable reference (beta)
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
+ with:
+ name: stable-reference-beta
+ path: api/stable/
+
+ - name: download next reference (beta)
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
+ with:
+ name: next-reference-beta
+ path: api/next/
diff --git a/.github/workflows/verify_microsite_accessibility-noop.yml b/.github/workflows/verify_microsite_accessibility-noop.yml
index d6f9d24b5e..26d4748772 100644
--- a/.github/workflows/verify_microsite_accessibility-noop.yml
+++ b/.github/workflows/verify_microsite_accessibility-noop.yml
@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml
index ee841f6fa8..01cf592a73 100644
--- a/.github/workflows/verify_microsite_accessibility.yml
+++ b/.github/workflows/verify_microsite_accessibility.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/verify_storybook-noop.yml b/.github/workflows/verify_storybook-noop.yml
deleted file mode 100644
index 392a218a83..0000000000
--- a/.github/workflows/verify_storybook-noop.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-# NO-OP placeholder that always passes for other paths
-# This is here so that we're able to set the status check as required
-
-name: Storybook Void
-on:
- pull_request:
- paths-ignore:
- - '.github/workflows/verify_storybook.yml'
- - 'storybook/**'
- - 'packages/config/src/**'
- - 'packages/theme/src/**'
- - 'packages/types/src/**'
- - 'packages/errors/src/**'
- - 'packages/version-bridge/src/**'
- - 'packages/test-utils/src/**'
- - 'packages/core-app-api/src/**'
- - 'packages/core-plugin-api/src/**'
- - 'packages/core-components/src/**'
- - '**/*.stories.tsx'
-
-permissions:
- contents: read
-
-jobs:
- noop:
- runs-on: ubuntu-latest
-
- name: Storybook
- steps:
- - name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
- with:
- egress-policy: audit
-
- - run: echo NOOP
diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml
deleted file mode 100644
index 3cc846a706..0000000000
--- a/.github/workflows/verify_storybook.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-name: Storybook
-on:
- # NOTE: If you change these you must update verify_storybook-noop.yml as well
- pull_request:
- paths:
- - '.github/workflows/verify_storybook.yml'
- - 'storybook/**'
- - 'packages/config/src/**'
- - 'packages/theme/src/**'
- - 'packages/types/src/**'
- - 'packages/errors/src/**'
- - 'packages/version-bridge/src/**'
- - 'packages/test-utils/src/**'
- - 'packages/core-app-api/src/**'
- - 'packages/core-plugin-api/src/**'
- - 'packages/core-components/src/**'
- - '**/*.stories.tsx'
-
-jobs:
- chromatic:
- runs-on: ${{ matrix.os }}
-
- strategy:
- matrix:
- os: [ubuntu-latest]
- node-version: [20.x]
-
- name: Storybook
- steps:
- - name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
- with:
- egress-policy: audit
-
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- fetch-depth: 0 # Required to retrieve git history
-
- - name: use node.js ${{ matrix.node-version }}
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- with:
- node-version: ${{ matrix.node-version }}
- registry-url: https://registry.npmjs.org/ # Needed for auth
- - name: yarn install
- uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
- with:
- cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- - name: storybook yarn install
- run: yarn install --immutable
- working-directory: storybook
-
- # Only for verification, the canon one is what we upload to Chromatic
- - run: yarn build-storybook
-
- - run: yarn --cwd packages/canon build-storybook
-
- - uses: chromaui/action@e8cc4c31775280b175a3c440076c00d19a9014d7 # v11
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- # projectToken intentionally shared to allow collaborators to run Chromatic on forks
- # https://www.chromatic.com/docs/custom-ci-provider#run-chromatic-on-external-forks-of-open-source-projects
- projectToken: chpt_dab72dc0f97d55b
- workingDir: packages/canon
- storybookBuildDir: storybook-static
diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml
index d7b172256d..187c3647d8 100644
--- a/.github/workflows/verify_windows.yml
+++ b/.github/workflows/verify_windows.yml
@@ -29,7 +29,7 @@ jobs:
steps:
- name: Harden Runner
- uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
+ uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.0
with:
egress-policy: audit
diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml
new file mode 100644
index 0000000000..5d47ba77da
--- /dev/null
+++ b/.github/workflows/welcome.yml
@@ -0,0 +1,42 @@
+name: Add a welcome comment
+
+on:
+ pull_request_target:
+ types: [opened]
+
+permissions:
+ issues: write
+ pull-requests: write
+ contents: read
+
+jobs:
+ welcome:
+ runs-on: ubuntu-latest
+ if: github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
+ steps:
+ - name: Add a welcome comment
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const pr = context.payload.pull_request
+ const login = pr.user.login
+
+ const message = `
+ Hi @${login}, thanks for opening your first pull request in Backstage! 👋
+
+ A couple of useful links to help you get started:
+
+ * [Contributing Guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)
+ * [DCO Sign-off Instructions](https://github.com/backstage/backstage/blob/master/DCO)
+ * [Style Guide](https://github.com/backstage/backstage/blob/master/STYLE.md)
+
+ We really appreciate your contribution and look forward to reviewing your work. Welcome aboard!
+ `
+
+ await github.rest.issues.createComment({
+ issue_number: pr.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: message
+ })
diff --git a/.gitignore b/.gitignore
index fa4184ecf2..aaf05b51cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -184,3 +184,6 @@ knip.json
type-docs
docs.json
tsconfig.typedoc.tmp.json
+
+# Storybook
+dist-storybook/
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
index 04ca7d780d..7269620fe6 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,13 +1,16 @@
+.cache
+.lighthouseci
.yarn
dist
microsite
-canon-docs/.next
-canon-docs/public
-canon-docs/out
-packages/canon/css
+docs-ui/.next
+docs-ui/public
+docs-ui/out
+packages/ui/css
coverage
*.hbs
templates
+type-docs
# old reports
api-report.md
api-report-*.md
diff --git a/.storybook/main.ts b/.storybook/main.ts
new file mode 100644
index 0000000000..788df83ba9
--- /dev/null
+++ b/.storybook/main.ts
@@ -0,0 +1,127 @@
+import type { StorybookConfig } from '@storybook/react-vite';
+
+import { join, dirname, posix } from 'path';
+
+const isChromatic = process.env.STORYBOOK_STORY_SET === 'chromatic';
+
+// All stories for full development
+const allStories = isChromatic
+ ? ['packages/ui']
+ : [
+ 'packages/ui',
+ 'packages/core-components',
+ 'packages/app',
+ 'plugins/org',
+ 'plugins/search',
+ 'plugins/search-react',
+ 'plugins/home',
+ 'plugins/catalog-react',
+ ];
+
+const rootPath = '../';
+const storiesSrcMdx = 'src/**/*.mdx';
+const storiesSrcGlob = 'src/**/*.stories.@(js|jsx|mjs|ts|tsx)';
+
+const getStoriesPath = (element: string, pattern: string) =>
+ posix.join(rootPath, element, pattern);
+
+const stories = allStories.flatMap(element => [
+ getStoriesPath(element, storiesSrcMdx),
+ getStoriesPath(element, storiesSrcGlob),
+]);
+
+// Resolve absolute path of a package. Needed in monorepos.
+function getAbsolutePath(value: string): any {
+ return dirname(require.resolve(join(value, 'package.json')));
+}
+
+const config: StorybookConfig = {
+ stories,
+ addons: [
+ getAbsolutePath('@storybook/addon-links'),
+ getAbsolutePath('@storybook/addon-themes'),
+ getAbsolutePath('@storybook/addon-docs'),
+ getAbsolutePath('@storybook/addon-a11y'),
+ ],
+ framework: {
+ name: getAbsolutePath('@storybook/react-vite'),
+ options: {},
+ },
+ viteFinal: async (config, { configType }) => {
+ // Add Node.js polyfills for browser compatibility
+ //
+ // When upgrading from Storybook 8 to 9 with the react-vite framework,
+ // Node.js polyfills are no longer automatically included by Vite.
+ // This causes "ReferenceError: process is not defined" errors in the browser
+ // when code tries to access Node.js globals like `process` and `util`.
+ //
+ // The @vitest/mocker (included with Storybook 9) expects MSW v2 APIs,
+ // but we want to keep MSW v1 for the rest of the monorepo to avoid
+ // breaking changes. This configuration provides the necessary polyfills
+ // and handles the MSW compatibility issue specifically for Storybook.
+ //
+ // These polyfills provide browser-compatible versions of Node.js globals:
+ // - process: Node.js process object with env
+ // - global -> globalThis: Maps Node.js global to browser's globalThis
+ //
+ // Without these, Backstage components that rely on Node.js APIs will fail
+ // to load in Storybook's browser environment.
+ // Different configurations for development vs production
+ if (configType === 'DEVELOPMENT') {
+ // Development: Include process polyfill to prevent runtime errors
+ config.define = {
+ ...config.define,
+ global: 'globalThis',
+ 'process.env': {},
+ process: '({ env: {}, browser: true })',
+ };
+ } else if (configType === 'PRODUCTION') {
+ // Production: Minimal define to avoid esbuild errors
+ config.define = {
+ ...config.define,
+ global: 'globalThis',
+ 'process.env': {},
+ // No process polyfill in production build
+ };
+ }
+
+ config.resolve = {
+ ...config.resolve,
+ alias: {
+ ...config.resolve?.alias,
+ // Provide Node.js polyfills for browser
+ process: 'process/browser',
+ util: 'util',
+ buffer: 'buffer',
+ stream: 'stream-browserify',
+ // Fix MSW v2 imports for @vitest/mocker compatibility
+ // @vitest/mocker expects MSW v2 APIs but we want to keep MSW v1 for the rest of the monorepo
+ 'msw/browser': join(__dirname, 'msw-browser-shim.js'),
+ 'msw/core/http': join(__dirname, 'msw-http-shim.js'),
+ },
+ };
+
+ // Optimize dependencies for better performance
+ config.optimizeDeps = {
+ ...config.optimizeDeps,
+ include: [
+ ...(config.optimizeDeps?.include || []),
+ 'process/browser',
+ 'util',
+ 'buffer',
+ 'stream-browserify',
+ ],
+ // Exclude MSW to prevent optimization conflicts with our shims
+ exclude: [
+ ...(config.optimizeDeps?.exclude || []),
+ 'msw',
+ 'msw/browser',
+ 'msw/core/http',
+ ],
+ };
+
+ return config;
+ },
+};
+
+export default config;
diff --git a/.storybook/modes.ts b/.storybook/modes.ts
new file mode 100644
index 0000000000..7087fa35eb
--- /dev/null
+++ b/.storybook/modes.ts
@@ -0,0 +1,18 @@
+export const allModes = {
+ 'light backstage': {
+ themeMode: 'light',
+ themeName: 'backstage',
+ },
+ 'dark backstage': {
+ themeMode: 'dark',
+ themeName: 'backstage',
+ },
+ 'light spotify': {
+ themeMode: 'light',
+ themeName: 'spotify',
+ },
+ 'dark spotify': {
+ themeMode: 'dark',
+ themeName: 'spotify',
+ },
+} as const;
diff --git a/.storybook/msw-browser-shim.js b/.storybook/msw-browser-shim.js
new file mode 100644
index 0000000000..e0c71e3d3e
--- /dev/null
+++ b/.storybook/msw-browser-shim.js
@@ -0,0 +1,47 @@
+// MSW v2 browser compatibility shim for @vitest/mocker
+// This provides MSW v2 exports using MSW v1 APIs to maintain compatibility
+// while keeping the rest of the monorepo on MSW v1
+
+try {
+ // Try to import from MSW v1
+ const { setupWorker, rest } = require('msw');
+
+ // Export in MSW v2 format expected by @vitest/mocker
+ module.exports = {
+ setupWorker,
+ // MSW v2 uses 'http' instead of 'rest', but we provide both for compatibility
+ http: rest,
+ rest, // Keep rest for any MSW v1 code
+ };
+} catch (error) {
+ // Fallback if MSW is not available - provide minimal mock
+ console.warn(
+ 'MSW not available, providing minimal mock for @vitest/mocker compatibility',
+ );
+ module.exports = {
+ setupWorker: () => ({
+ start: () => Promise.resolve(),
+ stop: () => {},
+ use: () => {},
+ resetHandlers: () => {},
+ }),
+ http: {
+ get: () => {},
+ post: () => {},
+ put: () => {},
+ delete: () => {},
+ patch: () => {},
+ head: () => {},
+ options: () => {},
+ },
+ rest: {
+ get: () => {},
+ post: () => {},
+ put: () => {},
+ delete: () => {},
+ patch: () => {},
+ head: () => {},
+ options: () => {},
+ },
+ };
+}
diff --git a/.storybook/msw-http-shim.js b/.storybook/msw-http-shim.js
new file mode 100644
index 0000000000..8653256960
--- /dev/null
+++ b/.storybook/msw-http-shim.js
@@ -0,0 +1,47 @@
+// MSW v2 http compatibility shim for @vitest/mocker
+// This provides MSW v2 http exports using MSW v1 APIs
+
+try {
+ // Try to import from MSW v1
+ const { rest } = require('msw');
+
+ // Export MSW v1 'rest' as MSW v2 'http' for @vitest/mocker compatibility
+ module.exports = {
+ http: rest,
+ // Provide individual methods that @vitest/mocker might expect
+ get: rest.get,
+ post: rest.post,
+ put: rest.put,
+ delete: rest.delete,
+ patch: rest.patch,
+ head: rest.head,
+ options: rest.options,
+ all: rest.all,
+ };
+} catch (error) {
+ // Fallback if MSW is not available
+ console.warn(
+ 'MSW not available, providing minimal http mock for @vitest/mocker compatibility',
+ );
+ const noop = () => {};
+ module.exports = {
+ http: {
+ get: noop,
+ post: noop,
+ put: noop,
+ delete: noop,
+ patch: noop,
+ head: noop,
+ options: noop,
+ all: noop,
+ },
+ get: noop,
+ post: noop,
+ put: noop,
+ delete: noop,
+ patch: noop,
+ head: noop,
+ options: noop,
+ all: noop,
+ };
+}
diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx
new file mode 100644
index 0000000000..8f987ebab1
--- /dev/null
+++ b/.storybook/preview.tsx
@@ -0,0 +1,145 @@
+import React, { useEffect } from 'react';
+import { TestApiProvider } from '@backstage/test-utils';
+import { Content, AlertDisplay } from '@backstage/core-components';
+import { apis } from './support/apis';
+import type { Decorator, Preview } from '@storybook/react-vite';
+import { useGlobals } from 'storybook/preview-api';
+import { UnifiedThemeProvider, themes } from '@backstage/theme';
+import { allModes } from './modes';
+
+// Default Backstage theme CSS (from packages/ui)
+import '../packages/ui/src/css/styles.css';
+
+// Custom Storybook chrome/styles
+import './storybook.css';
+
+// Custom themes
+import './themes/spotify.css';
+
+const preview: Preview = {
+ globalTypes: {
+ themeMode: {
+ name: 'Theme Mode',
+ description: 'Global theme mode for components',
+ defaultValue: 'light',
+ toolbar: {
+ icon: 'circlehollow',
+ items: [
+ { value: 'light', icon: 'circlehollow', title: 'Light' },
+ { value: 'dark', icon: 'circle', title: 'Dark' },
+ ],
+ showName: true,
+ dynamicTitle: true,
+ },
+ },
+ themeName: {
+ name: 'Theme Name',
+ description: 'Global theme name for components',
+ defaultValue: 'backstage',
+ toolbar: {
+ icon: 'paintbrush',
+ items: [
+ { value: 'backstage', title: 'Backstage' },
+ { value: 'spotify', title: 'Spotify' },
+ ],
+ showName: true,
+ dynamicTitle: true,
+ },
+ },
+ },
+ initialGlobals: {
+ themeMode: 'light',
+ themeName: 'backstage',
+ },
+ parameters: {
+ layout: 'fullscreen',
+
+ backgrounds: {
+ disable: true,
+ },
+
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+
+ options: {
+ storySort: {
+ order: ['Backstage UI', 'Plugins', 'Layout', 'Navigation'],
+ },
+ },
+
+ viewport: {
+ viewports: {
+ initial: {
+ name: 'Initial',
+ styles: { width: '320px', height: '100%' },
+ },
+ xs: { name: 'Extra Small', styles: { width: '640px', height: '100%' } },
+ sm: { name: 'Small', styles: { width: '768px', height: '100%' } },
+ md: { name: 'Medium', styles: { width: '1024px', height: '100%' } },
+ lg: { name: 'Large', styles: { width: '1280px', height: '100%' } },
+ xl: {
+ name: 'Extra Large',
+ styles: { width: '1536px', height: '100%' },
+ },
+ },
+ },
+
+ docs: {
+ codePanel: true,
+ },
+
+ chromatic: {
+ modes: {
+ 'light backstage': allModes['light backstage'],
+ // TODO: Enable these modes when we have more Chromatic snapshots.
+ // 'dark backstage': allModes['dark backstage'],
+ // 'light spotify': allModes['light spotify'],
+ // 'dark spotify': allModes['dark spotify'],
+ },
+ },
+ },
+ decorators: [
+ Story => {
+ const [globals] = useGlobals();
+ const selectedTheme =
+ globals.themeMode === 'light' ? themes.light : themes.dark;
+ const selectedThemeMode = globals.themeMode || 'light';
+ const selectedThemeName = globals.themeName || 'backstage';
+
+ useEffect(() => {
+ document.body.removeAttribute('data-theme-mode');
+ document.body.removeAttribute('data-theme-name');
+ document.body.setAttribute('data-theme-mode', selectedThemeMode);
+ document.body.setAttribute('data-theme-name', selectedThemeName);
+ return () => {
+ document.body.removeAttribute('data-theme-mode');
+ document.body.removeAttribute('data-theme-name');
+ };
+ }, [selectedTheme, selectedThemeName]);
+
+ document.body.style.backgroundColor = 'var(--bui-bg)';
+ const docsStoryElements = document.getElementsByClassName('docs-story');
+ Array.from(docsStoryElements).forEach(element => {
+ (element as HTMLElement).style.backgroundColor = 'var(--bui-bg)';
+ });
+
+ return (
+
+ {/* @ts-ignore */}
+
+
+
+
+
+
+
+ );
+ },
+ ],
+};
+
+export default preview;
diff --git a/.storybook/storybook.css b/.storybook/storybook.css
new file mode 100644
index 0000000000..e69c4a8eb1
--- /dev/null
+++ b/.storybook/storybook.css
@@ -0,0 +1,38 @@
+:root {
+ --sb-panel-radius: 0;
+ --sb-panel-top: 0;
+ --sb-panel-bottom: 0;
+ --sb-panel-left: 0;
+ --sb-panel-right: 0;
+ --sb-sidebar-border: none;
+ --sb-sidebar-border-right: 1px solid var(--bui-border);
+ --sb-sidebar-bg: #000;
+ --sb-options-border: none;
+ --sb-options-border-left: 1px solid var(--bui-border);
+ --sb-content-padding-inline: 250px;
+}
+
+[data-theme-mode='dark'] {
+ --sb-sidebar-bg: var(--bui-bg-surface-1);
+}
+
+[data-theme-name='spotify'] {
+ --sb-panel-radius: var(--bui-radius-3);
+ --sb-panel-top: 8px;
+ --sb-panel-bottom: 8px;
+ --sb-panel-left: 8px;
+ --sb-panel-right: 8px;
+ --sb-sidebar-border: none;
+ --sb-sidebar-border-right: none;
+ --sb-sidebar-bg: var(--bui-bg-surface-1);
+ --sb-options-border: none;
+ --sb-options-border-left: none;
+ --sb-content-padding-inline: 258px;
+}
+
+[data-theme-name='spotify'][data-theme-mode='light'] {
+ --sb-sidebar-border: 1px solid var(--bui-border);
+ --sb-sidebar-border-right: 1px solid var(--bui-border);
+ --sb-options-border: 1px solid var(--bui-border);
+ --sb-options-border-left: 1px solid var(--bui-border);
+}
diff --git a/storybook/.storybook/apis.js b/.storybook/support/apis.js
similarity index 100%
rename from storybook/.storybook/apis.js
rename to .storybook/support/apis.js
diff --git a/.storybook/themes/spotify.css b/.storybook/themes/spotify.css
new file mode 100644
index 0000000000..bcb1ded9ff
--- /dev/null
+++ b/.storybook/themes/spotify.css
@@ -0,0 +1,288 @@
+@font-face {
+ font-family: CircularSpTitle;
+ font-weight: 900;
+ font-display: swap;
+ unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-017E, U+018F, U+0192,
+ U+01A0-01A1, U+01AF-01B0, U+01B5-01B6, U+01C4-01C6, U+01F1-01F3, U+01FA-01FF,
+ U+0218-021B, U+0237, U+0259, U+02BB-02BC, U+02C6-02C7, U+02C9, U+02D8-02DD,
+ U+0300-0301, U+0303, U+0309, U+0323, U+0394, U+03A9, U+03BC, U+03C0,
+ U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6, U+2007-2008, U+200B,
+ U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026,
+ U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044, U+2051, U+2070,
+ U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117, U+2122, U+2126,
+ U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9, U+21B0-21B5,
+ U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215, U+2219-221A, U+221E,
+ U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5, U+25A0-25A1, U+25AF,
+ U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713, U+2715, U+2780-2788,
+ U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C, U+FF0E, U+FF1A-FF1B,
+ U+FFFF;
+ src: url('https://encore.scdn.co/fonts/CircularSpTitle-Black-4588c99025b967475c31695b0743dd1a.woff2')
+ format('woff2'),
+ url('https://encore.scdn.co/fonts/CircularSpTitle-Black-506746f387a26f25aa3d023b3e501d34.woff')
+ format('woff');
+}
+@font-face {
+ font-family: CircularSpTitle;
+ font-weight: 700;
+ font-display: swap;
+ unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-017E, U+018F, U+0192,
+ U+01A0-01A1, U+01AF-01B0, U+01B5-01B6, U+01C4-01C6, U+01F1-01F3, U+01FA-01FF,
+ U+0218-021B, U+0237, U+0259, U+02BB-02BC, U+02C6-02C7, U+02C9, U+02D8-02DD,
+ U+0300-0301, U+0303, U+0309, U+0323, U+0394, U+03A9, U+03BC, U+03C0,
+ U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6, U+2007-2008, U+200B,
+ U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026,
+ U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044, U+2051, U+2070,
+ U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117, U+2122, U+2126,
+ U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9, U+21B0-21B5,
+ U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215, U+2219-221A, U+221E,
+ U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5, U+25A0-25A1, U+25AF,
+ U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713, U+2715, U+2780-2788,
+ U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C, U+FF0E, U+FF1A-FF1B,
+ U+FFFF;
+ src: url('https://encore.scdn.co/fonts/CircularSpTitle-Bold-b2586b06a2e1522e9d879d84c2792a58.woff2')
+ format('woff2'),
+ url('https://encore.scdn.co/fonts/CircularSpTitle-Bold-1624fb2df28c20d7203d7fb86ce8b481.woff')
+ format('woff');
+}
+@font-face {
+ font-family: CircularSp;
+ font-weight: 700;
+ font-display: swap;
+ unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-017E, U+018F, U+0192,
+ U+01A0-01A1, U+01AF-01B0, U+01B5-01B6, U+01C4-01C6, U+01F1-01F3, U+01FA-01FF,
+ U+0218-021B, U+0237, U+0259, U+02BB-02BC, U+02C6-02C7, U+02C9, U+02D8-02DD,
+ U+0300-0301, U+0303, U+0309, U+0323, U+0394, U+03A9, U+03BC, U+03C0,
+ U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6, U+2007-2008, U+200B,
+ U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026,
+ U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044, U+2051, U+2070,
+ U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117, U+2122, U+2126,
+ U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9, U+21B0-21B5,
+ U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215, U+2219-221A, U+221E,
+ U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5, U+25A0-25A1, U+25AF,
+ U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713, U+2715, U+2780-2788,
+ U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C, U+FF0E, U+FF1A-FF1B,
+ U+FFFF;
+ src: url('https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2')
+ format('woff2'),
+ url('https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff')
+ format('woff');
+}
+@font-face {
+ font-family: CircularSp;
+ font-weight: 400;
+ font-display: swap;
+ unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-017E, U+018F, U+0192,
+ U+01A0-01A1, U+01AF-01B0, U+01B5-01B6, U+01C4-01C6, U+01F1-01F3, U+01FA-01FF,
+ U+0218-021B, U+0237, U+0259, U+02BB-02BC, U+02C6-02C7, U+02C9, U+02D8-02DD,
+ U+0300-0301, U+0303, U+0309, U+0323, U+0394, U+03A9, U+03BC, U+03C0,
+ U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6, U+2007-2008, U+200B,
+ U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026,
+ U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044, U+2051, U+2070,
+ U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117, U+2122, U+2126,
+ U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9, U+21B0-21B5,
+ U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215, U+2219-221A, U+221E,
+ U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5, U+25A0-25A1, U+25AF,
+ U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713, U+2715, U+2780-2788,
+ U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C, U+FF0E, U+FF1A-FF1B,
+ U+FFFF;
+ src: url('https://encore.scdn.co/fonts/CircularSp-Book-a00e99ef9996a3a157fb6b746856d04f.woff2')
+ format('woff2'),
+ url('https://encore.scdn.co/fonts/CircularSp-Book-3f73da7d35bd81c706bce7bbb84964de.woff')
+ format('woff');
+}
+
+@font-face {
+ font-family: CircularSp;
+ font-weight: 700;
+ font-display: swap;
+ unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-017E, U+018F, U+0192,
+ U+01A0-01A1, U+01AF-01B0, U+01B5-01B6, U+01C4-01C6, U+01F1-01F3, U+01FA-01FF,
+ U+0218-021B, U+0237, U+0259, U+02BB-02BC, U+02C6-02C7, U+02C9, U+02D8-02DD,
+ U+0300-0301, U+0303, U+0309, U+0323, U+0394, U+03A9, U+03BC, U+03C0,
+ U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6, U+2007-2008, U+200B,
+ U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026,
+ U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044, U+2051, U+2070,
+ U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117, U+2122, U+2126,
+ U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9, U+21B0-21B5,
+ U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215, U+2219-221A, U+221E,
+ U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5, U+25A0-25A1, U+25AF,
+ U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713, U+2715, U+2780-2788,
+ U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C, U+FF0E, U+FF1A-FF1B,
+ U+FFFF;
+ src: url('https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2')
+ format('woff2'),
+ url('https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff')
+ format('woff');
+}
+
+[data-theme-name='spotify'] {
+ --bui-font-text: CircularSp, CircularSp-Arab, CircularSp-Hebr, CircularSp-Cyrl,
+ CircularSp-Grek, CircularSp-Deva;
+ --bui-font-title: CircularSpTitle, CircularSp-Arab, CircularSp-Hebr,
+ CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
+ --bui-font-regular: CircularSp, CircularSp-Arab, CircularSp-Hebr,
+ CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
+
+ .bui-Button {
+ border-radius: var(--bui-radius-3);
+ padding-inline: var(--bui-space-3);
+ }
+
+ .bui-ButtonIcon {
+ padding: 0;
+ }
+
+ .bui-MenuPopup {
+ box-sizing: border-box;
+ max-width: 21.25rem;
+ }
+
+ .bui-MenuSubmenuTrigger,
+ .bui-MenuItem {
+ height: auto;
+ min-height: 2rem;
+ }
+
+ .bui-Text {
+ font-family: var(--bui-font-text);
+ }
+
+ .bui-Heading {
+ font-family: var(--bui-font-title);
+ }
+
+ .bui-TableRow {
+ border: none;
+ border-radius: var(--bui-radius-4);
+ }
+
+ .bui-TableRow:hover td:first-child {
+ border-top-left-radius: var(--bui-radius-2);
+ border-bottom-left-radius: var(--bui-radius-2);
+ }
+
+ .bui-TableRow:hover td:last-child {
+ border-top-right-radius: var(--bui-radius-2);
+ border-bottom-right-radius: var(--bui-radius-2);
+ }
+
+ .bui-TableBody:before,
+ .bui-TableBody:after {
+ line-height: var(--bui-space-1);
+ content: '\200C';
+ display: block;
+ }
+
+ .bui-TableHeader .bui-TableRow {
+ border-bottom: 1px solid var(--bui-border);
+ }
+
+ .bui-TableHead {
+ font-size: var(--bui-font-size-2);
+ color: var(--bui-fg-secondary);
+ font-weight: var(--bui-font-weight-regular);
+ }
+
+ .bui-HeaderToolbar {
+ padding-top: var(--bui-space-2);
+ padding-inline: var(--bui-space-2);
+ }
+
+ .bui-HeaderToolbarWrapper {
+ border-radius: var(--bui-radius-3);
+ padding-inline: var(--bui-space-3);
+ border: none;
+ }
+
+ .bui-HeaderToolbarControls {
+ right: calc(var(--bui-space-3) - 1px);
+ }
+
+ .bui-HeaderTabsWrapper {
+ margin-top: var(--bui-space-2);
+ margin-inline: var(--bui-space-2);
+ border-radius: var(--bui-radius-3);
+ padding-inline: var(--bui-space-1);
+ border: none;
+ }
+
+ .bui-Input {
+ border-radius: var(--bui-radius-3);
+ }
+
+ .bui-Tag {
+ border-radius: var(--bui-radius-full);
+ }
+}
+
+[data-theme-mode='light'][data-theme-name='spotify'] {
+ --bui-bg: var(--bui-gray-1);
+ --bui-bg-surface-1: var(--bui-white);
+ --bui-bg-surface-2: var(--bui-gray-2);
+ --bui-bg-solid: #1ed760;
+ --bui-bg-solid-hover: #3be477;
+ --bui-bg-solid-pressed: #1abc54;
+ --bui-bg-solid-disabled: var(--bui-gray-2);
+
+ --bui-fg-primary: var(--bui-black);
+ --bui-fg-secondary: var(--bui-gray-7);
+ --bui-fg-solid: var(--bui-black);
+
+ --bui-border: var(--bui-gray-3);
+ --bui-border-hover: rgba(0, 0, 0, 0.3);
+ --bui-border-pressed: rgba(0, 0, 0, 0.5);
+ --bui-border-disabled: rgba(0, 0, 0, 0.1);
+ --bui-border-danger: #f87a7a;
+ --bui-border-warning: #e36d05;
+ --bui-border-success: #53db83;
+
+ --bui-ring: rgba(0, 0, 0, 0.2);
+
+ .bui-HeaderToolbarWrapper {
+ border: 1px solid var(--bui-border);
+ }
+
+ .bui-HeaderTabsWrapper {
+ border: 1px solid var(--bui-border);
+ }
+}
+
+[data-theme-mode='dark'][data-theme-name='spotify'] {
+ --bui-bg: var(--bui-black);
+ --bui-bg-surface-1: var(--bui-gray-1);
+ --bui-bg-surface-2: var(--bui-gray-2);
+ --bui-bg-solid: #1ed760;
+ --bui-bg-solid-hover: #3be477;
+ --bui-bg-solid-pressed: #1abc54;
+ --bui-bg-solid-disabled: #0f6c30;
+ --bui-bg-tint: #242424;
+ --bui-bg-tint-hover: #202020;
+ --bui-bg-tint-pressed: #292929;
+ --bui-bg-tint-disabled: rgba(255, 255, 255, 0.8);
+ --bui-bg-danger: #3b1219;
+ --bui-bg-warning: #302008;
+ --bui-bg-success: #132d21;
+
+ --bui-fg-primary: var(--bui-white);
+ --bui-fg-secondary: var(--bui-gray-7);
+ --bui-fg-link: var(--bui-white);
+ --bui-fg-link-hover: var(--bui-white);
+ --bui-fg-disabled: var(--bui-gray-5);
+ --bui-fg-solid: var(--bui-black);
+ --bui-fg-solid-disabled: #083618;
+ --bui-fg-tint: var(--bui-white);
+ --bui-fg-tint-disabled: var(--bui-gray-5);
+ --bui-fg-danger: #e22b2b;
+ --bui-fg-warning: #e36d05;
+ --bui-fg-success: #1db954;
+
+ --bui-border: var(--bui-gray-3);
+ --bui-border-hover: rgba(255, 255, 255, 0.4);
+ --bui-border-pressed: rgba(255, 255, 255, 0.5);
+ --bui-border-disabled: rgba(255, 255, 255, 0.2);
+ --bui-border-danger: #f87a7a;
+ --bui-border-warning: #e36d05;
+ --bui-border-success: #53db83;
+
+ --bui-ring: rgba(255, 255, 255, 0.2);
+}
diff --git a/.yarn/patches/ast-types-npm-0.16.1-43c4ac4b0d.patch b/.yarn/patches/ast-types-npm-0.16.1-43c4ac4b0d.patch
new file mode 100644
index 0000000000..996a23c205
--- /dev/null
+++ b/.yarn/patches/ast-types-npm-0.16.1-43c4ac4b0d.patch
@@ -0,0 +1,15 @@
+diff --git a/lib/main.d.ts b/lib/main.d.ts
+index 9b54fef0086ea0c99368d8a95f2476e5dcd6d9c1..5c3853901e522c445cdb23b07538aad8a15bafb0 100644
+--- a/lib/main.d.ts
++++ b/lib/main.d.ts
+@@ -1,7 +1,7 @@
+-import { ASTNode, Type, AnyType, Field } from "./types";
+-import { NodePath } from "./node-path";
++import { ASTNode, type Type, AnyType, Field } from "./types";
++import { type NodePath } from "./node-path";
+ import { namedTypes } from "./gen/namedTypes";
+-import { builders } from "./gen/builders";
++import { type builders } from "./gen/builders";
+ import { Visitor } from "./gen/visitor";
+ declare const astNodesAreEquivalent: {
+ (a: any, b: any, problemPath?: any): boolean;
diff --git a/ADOPTERS.md b/ADOPTERS.md
index e97c213adf..208cd370ba 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -284,3 +284,6 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [Scalepoint](https://scalepoint.com) | [Yuriy Ostapenko](https://github.com/yuriyostapenko), [Dmytro Pasko](https://github.com/dimapasko), [Yuri Sedykh ](https://github.com/yuiri-sedykh), [Bartosz Wisniewski](https://github.com/Epgor) | Internal Developer Portal: Software Catalog with infrastructure resources and dependencies, Software Templates, TechDocs, Tech Radar, Kubernetes and Observability, as well as custom CI/CD components |
| [VR](https://www.vr.com.br) | [Felipe Gomes](https://www.linkedin.com/in/felipe-gomes-da-silva), [Tiago Garcia](https://www.linkedin.com/in/tiago-garcia-6301a925) | Internal portal for development squads, service catalog, documentation hub, and application mapping for the entire company ecosystem, simplifying cloud infrastructure construction through templates. |
| [Plata](https://platacard.mx/en) | [Shamil Ganiev](https://github.com/ganievs) | Internal Developer Portal for the whole engineering team |
+| [Syngenta Digital](https://www.syngentadigital.com) | [Bitan Mallick](https://www.linkedin.com/in/bitanmallick) | Internal developer portal, designed to empower developers and streamline workflows. We use software catalog, tech-radar, software templates, tech-docs and various custom plugins to ensure an efficient and collaborative development experience. |
+| [Sophotech](https://sopho.tech) | [@archy-rock3t-cloud](https://github.com/archy-rock3t-cloud), [Artem Muterko](mailto:artem@sopho.tech) | Custom Developer Platform based on Backstage, providing a service catalog, infrastructure templates, and integrated tooling to give developers a self-service experience. |
+| [Swiss Mobiliar Insurance Company Ltd.](https://www.mobiliar.ch/) | [Patrick Wyler](mailto:patrick.wyler@mobiliar.ch) and [Beat Winistörfer](mailto:beat.winistoerfer@mobiliar.ch) | The portal provides a unified interface for accessing all relevant DevOps information previously scattered across various locations, enhancing accessibility and clarity for all IT employees. It relies on an internal graph database that enhances the Backstage software catalog with many additional elements. Significant effort has been invested in the visual representation of information through graphs and diagrams, facilitating analysis and improving the understanding of dependencies. |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9413206197..ba40be86c8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -102,6 +102,16 @@ Visit and you should see the bleeding edge of Backstage
If you want to get a better understanding of the layout of the repo now that you have a local copy running feel free to review the [Backstage Project Structure](https://backstage.io/docs/getting-started/#general-folder-structure) documentation.
+#### Using Docker for the Example App
+
+You can run the Example App using Docker with Postgres, OpenSearch and Redis services. This setup very closely resembles how Backstage is run in production in many occasions.
+
+To start the Example App with Docker, make sure you have Docker and Docker Compose installed on your machine, then run the following command from the root of the repository:
+
+```bash
+yarn start:docker
+```
+
## Coding Guidelines
All code is formatted with `prettier` using the configuration in the repo. If possible, we recommend configuring your editor to format automatically, but you can also use the `yarn prettier --write ` command to format files.
diff --git a/LABELS.md b/LABELS.md
index 7f8804f75a..f1af06d4fa 100644
--- a/LABELS.md
+++ b/LABELS.md
@@ -26,6 +26,7 @@ These labels indicate what is needed to move an issue forward before it can be a
- `needs:bep` - The issue is an advanced addition that needs a [Backstage Enhancement Proposal](./beps/README.md).
- `needs:direction` - The issue needs direction from the owners of the area.
+- `needs:discussion` - The way forward for this issue is unclear and it needs further discussion with the author and other participants.
- `needs:more-info` - The issue needs more information from the author.
- `needs:motivation` - It is not clear why this change is needed. The author should provide motivation for the change, for instance by giving examples of concrete use cases or scenarios.
- `needs:repro` - The issue cannot be reproduced by the owners of the area. The author should provide more information to help them reproduce the issue, if possible with a minimal reproduction repository.
@@ -39,7 +40,7 @@ These labels indicate which part of Backstage an issue or pull request relates t
- `area:auditor` - Auditor service and it's use in plugins.
- `area:auth` - Authentication and 3rd party authorization.
- `area:catalog` - The Catalog plugin and the Software Catalog model and integrations.
-- `area:design-system` - The Canon design system and library.
+- `area:design-system` - The Backstage UI design system and library.
- `area:documentation` - Documentation for adopters, users, and developers.
- `area:events` - The Events system and integrations for other plugins.
- `area:framework` - The core Backstage framework.
@@ -81,11 +82,17 @@ These labels indicate that an issue is related to specific domains of expertise.
- `domain:tooling` - Tooling and automation in Node.js and GitHub Actions.
- `domain:web` - Frontend development using TypeScript and React.
+## Workflow Labels
+
+These labels indicate a workflow status for the owners.
+
+- `workflow:do-not-merge` - The Pull Request should not be merged.
+- `workflow:before-release` - Should be handled before the next main-line release.
+- `workflow:after-release` - Should be handled after the next main-line release.
+- `workflow:after-vacations` - To be handled once the owners return from vacation.
+
## General Labels
-- `after vacations` - To be handled once the owners return from vacation.
-- `do not merge` - The pull request should not be merged.
-- `fix before release` - Should be handled before the next release.
- `good first issue` - Good for new contributors.
- `stale` - The issue or pull request has not seen any activity for a while and will be closed if no further activity is seen.
- `no stale` - The issue or pull request should not be closed due to inactivity.
diff --git a/OWNERS.md b/OWNERS.md
index 85ffadae42..d4ee3c6400 100644
--- a/OWNERS.md
+++ b/OWNERS.md
@@ -56,6 +56,18 @@ Scope: The Backstage design system, component library, as well as surrounding to
| Charles de Dreuille | Spotify | | [cdedreuille](https://github.com/cdedreuille) | `cdedreuille` |
| Patrik Oldsberg | Spotify | Cubic Belugas | [Rugvip](https://github.com/Rugvip) | `Rugvip` |
+### Documentation
+
+Team: @backstage/documentation-maintainers
+
+Scope: The Backstage Documentation and Microsite, excluding the plugins listing
+
+| Name | Organization | GitHub | Discord |
+| --------------- | ------------- | ----------------------------------------------------- | ------------- |
+| Andre Wanlin | Spotify | [awanlin](https://github.com/awanlin) | `ahhhndre` |
+| Aramis Sennyey | DoorDash | [aramissennyeydd](https://github.com/aramissennyeydd) | `Aramis#7984` |
+| Peter Macdonald | VodafoneZiggo | [Parsifal-M](https://github.com/Parsifal-M) | `parsifal` |
+
### Framework
Team: @backstage/framework-maintainers
@@ -77,11 +89,11 @@ Team: @backstage/helm-chart-maintainers
Scope: The Backstage [Helm Chart(s)](https://github.com/backstage/charts).
-| Name | Organization | Team | GitHub | Discord |
-| -------------------- | ------------ | ---- | ---------------------------------------- | -------------- |
-| Andrew Block | Red Hat | | [sabre1041](http://github.com/sabre1041) | sabre1041#2622 |
-| Tom Coufal | Red Hat | | [tumido](http://github.com/tumido) | Tumi#4346 |
-| Vincenzo Scamporlino | Spotify | | [vinzscam](http://github.com/vinzscam) | vinzscam#6944 |
+| Name | Organization | Team | GitHub | Discord |
+| -------------------- | ------------ | ---- | ---------------------------------------- | ---------------- |
+| Andrew Block | Red Hat | | [sabre1041](http://github.com/sabre1041) | `sabre1041#2622` |
+| Tom Coufal | Red Hat | | [tumido](http://github.com/tumido) | `Tumi#4346` |
+| Vincenzo Scamporlino | Spotify | | [vinzscam](http://github.com/vinzscam) | `vinzscam#6944` |
### Home
@@ -89,12 +101,14 @@ Team: @backstage/home-maintainers
Scope: The Backstage home page and information architecture
-| Name | Organization | Team | GitHub | Discord |
-| ------------------------ | ------------ | -------------- | ---------------------------------------- | ----------------- |
-| Avantika Iyer | Spotify | Infinite Buck$ | [tikabom](http://github.com/tikabom) | - |
-| Emma Indal | Spotify | Infinite Buck$ | [emmaindal](http://github.com/emmaindal) | emmaindal#7503 |
-| Djamaile Rahamat | Spotify | Infinite Buck$ | [djamaile](http://github.com/djamaile) | dyoqi |
-| Raghunandan Balachandran | Spotify | Infinite Buck$ | [soapraj](http://github.com/soapraj) | raghunandanb#1114 |
+| Name | Organization | Team | GitHub | Discord |
+| ------------------ | ------------ | --------------- | ----------------------------------------- | ------- |
+| Ainhoa Larumbe | Spotify | Imaginary Goats | [ainhoaL](http://github.com/ainhoaL) | - |
+| Avantika Iyer | Spotify | Imaginary Goats | [tikabom](http://github.com/tikabom) | - |
+| Daniel Jelčić | Spotify | Imaginary Goats | [djelcic](http://github.com/danieljelcic) | - |
+| Eric Peterson | Spotify | Imaginary Goats | [iamEAP](http://github.com/iamEAP) | - |
+| Musaab Elfaqih | Spotify | Imaginary Goats | [TheSabby](http://github.com/TheSabby) | - |
+| Landry Zinda-Snead | Spotify | Imaginary Goats | [landryzs](http://github.com/landryzs) | - |
### Kubernetes
@@ -206,18 +220,6 @@ Scope: Tooling and Community Repo Maintainers for the Backstage [Community Plugi
| Vincenzo Scamporlino | Spotify | [vinzscam](https://github.com/vinzscam) | `vinzscam` |
| Peter Macdonald | VodafoneZiggo | [Parsifal-M](https://github.com/Parsifal-M) | `parsifal` |
-### Documentation
-
-Team: @backstage/documentation-maintainers
-
-Scope: The Backstage Documentation and Microsite, excluding the plugins listing
-
-| Name | Organization | GitHub | Discord |
-| --------------- | ------------- | ----------------------------------------------------- | ------------- |
-| Andre Wanlin | Spotify | [awanlin](https://github.com/awanlin) | `ahhhndre` |
-| Aramis Sennyey | DoorDash | [aramissennyeydd](https://github.com/aramissennyeydd) | `Aramis#7984` |
-| Peter Macdonald | VodafoneZiggo | [Parsifal-M](https://github.com/Parsifal-M) | `parsifal` |
-
### Events
Team: @backstage/events-maintainers
diff --git a/README.md b/README.md
index e3e9bd77dd..ba44ca951d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
+> [!NOTE]
+> 🏖 From Monday October 27th through November 3rd, maintainers and Spotify employees will be on vacation due to Wellness Week. Expect the project to move a little slower than normal, and support to be limited. Normal service will resume after that! 🏝
+
[](https://backstage.io/)
# [Backstage](https://backstage.io)
diff --git a/app-config.docker.yaml b/app-config.docker.yaml
new file mode 100644
index 0000000000..07f7c7ac8c
--- /dev/null
+++ b/app-config.docker.yaml
@@ -0,0 +1,23 @@
+# To run local backstage instance with docker services,
+# run:
+# 1. `docker compose -f docker-compose.deps.yml up --wait`
+# 2. `BACKSTAGE_ENV=docker yarn dev`
+backend:
+ database:
+ client: pg
+ connection:
+ host: localhost
+ port: 5432
+ user: postgres
+ password: postgres
+ cache:
+ store: redis
+ connection: redis://localhost:6379
+
+search:
+ elasticsearch:
+ provider: opensearch
+ node: 'http://localhost:9200'
+ auth:
+ username: admin
+ password: admin
diff --git a/app-config.yaml b/app-config.yaml
index 89e417fad3..b45255d26c 100644
--- a/app-config.yaml
+++ b/app-config.yaml
@@ -1,8 +1,7 @@
app:
title: Backstage Example App
baseUrl: http://localhost:3000
- experimental:
- packages: all # ✨
+ packages: all # ✨
#datadogRum:
# clientToken: '123456789'
@@ -36,6 +35,12 @@ backend:
# keys:
# - secret: ${BACKEND_SECRET}
+ # Used for testing rate limiting locally
+ # rateLimit:
+ # windowMs: 1m
+ # incomingRequestLimit: 1
+ # ipAllowList: []
+
auth:
# TODO: once plugins have been migrated we can remove this, but right now it
# is require for the backend-next to work in this repo
@@ -60,87 +65,17 @@ backend:
- host: example.com
- host: '*.mozilla.org'
# workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir
-
+ actions:
+ pluginSources:
+ - catalog
# See README.md in the proxy-backend plugin for information on the configuration format
proxy:
endpoints:
- '/circleci/api':
- target: https://circleci.com/api/v1.1
- headers:
- Circle-Token: ${CIRCLECI_AUTH_TOKEN}
-
- '/jenkins/api':
- target: http://localhost:8080
- headers:
- Authorization: ${JENKINS_BASIC_AUTH_HEADER}
-
- '/travisci/api':
- target: https://api.travis-ci.com
- changeOrigin: true
- headers:
- Authorization: ${TRAVISCI_AUTH_TOKEN}
- travis-api-version: '3'
-
- '/newrelic/apm/api':
- target: https://api.newrelic.com/v2
- headers:
- X-Api-Key: ${NEW_RELIC_REST_API_KEY}
- allowedHeaders:
- - link
-
- '/newrelic/api':
- target: https://api.newrelic.com
- headers:
- X-Api-Key: ${NEW_RELIC_USER_KEY}
-
'/pagerduty':
target: https://api.pagerduty.com
headers:
Authorization: Token token=${PAGERDUTY_TOKEN}
- '/buildkite/api':
- target: https://api.buildkite.com/v2/
- headers:
- Authorization: ${BUILDKITE_TOKEN}
-
- '/sentry/api':
- target: https://sentry.io/api/
- allowedMethods: ['GET']
- headers:
- Authorization: ${SENTRY_TOKEN}
-
- '/ilert':
- target: https://api.ilert.com
- allowedMethods: ['GET', 'POST', 'PUT']
- allowedHeaders: ['Authorization']
- headers:
- Authorization: ${ILERT_AUTH_HEADER}
-
- '/airflow':
- target: https://your.airflow.instance.com/api/v1
- headers:
- Authorization: ${AIRFLOW_BASIC_AUTH_HEADER}
-
- '/gocd':
- target: https://your.gocd.instance.com/go/api
- allowedMethods: ['GET']
- allowedHeaders: ['Authorization']
- headers:
- Authorization: Basic ${GOCD_AUTH_CREDENTIALS}
-
- '/dynatrace':
- target: https://your.dynatrace.instance.com/api/v2
- headers:
- Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}'
-
- '/stackstorm':
- target: https://your.stackstorm.instance.com/api
- headers:
- St2-Api-Key: ${ST2_API_KEY}
-
- '/puppetdb':
- target: https://your.puppetdb.instance.com
-
organization:
name: My Company
@@ -157,46 +92,6 @@ techdocs:
publisher:
type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage' or 'openStackSwift'. Read documentation for using alternatives.
-dynatrace:
- baseUrl: https://your.dynatrace.instance.com
-
-nomad:
- addr: 0.0.0.0
-
-# Score-cards sample configuration.
-scorecards:
- jsonDataUrl: https://raw.githubusercontent.com/Oriflame/backstage-plugins/main/plugins/score-card/sample-data/
- wikiLinkTemplate: https://link-to-wiki/{id}
-
-sentry:
- organization: my-company
-
-rollbar:
- organization: my-company
- # NOTE: The rollbar-backend & accountToken key may be deprecated in the future (replaced by a proxy config)
- accountToken: my-rollbar-account-token
-
-lighthouse:
- baseUrl: http://localhost:3003
-
-kubernetes:
- serviceLocatorMethod:
- type: 'multiTenant'
- clusterLocatorMethods:
- - type: 'config'
- clusters: []
-
-kafka:
- clientId: backstage
- clusters:
- - name: cluster
- dashboardUrl: https://akhq.io/
- brokers:
- - localhost:9092
-
-allure:
- baseUrl: http://localhost:5050/allure-docker-service
-
integrations:
github:
- host: github.com
@@ -244,7 +139,6 @@ integrations:
secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
catalog:
- useUrlReadersSearch: true
import:
entityFilename: catalog-info.yaml
pullRequestBranchName: backstage-integration
@@ -269,26 +163,6 @@ catalog:
plugins:
- catalog
- search
-
- processors:
- ldapOrg:
- ### Example for how to add your enterprise LDAP server
- # providers:
- # - target: ldaps://ds.example.net
- # bind:
- # dn: uid=ldap-reader-user,ou=people,ou=example,dc=example,dc=net
- # secret: ${LDAP_SECRET}
- # users:
- # dn: ou=people,ou=example,dc=example,dc=net
- # options:
- # filter: (uid=*)
- # map:
- # description: l
- # groups:
- # dn: ou=access,ou=groups,ou=example,dc=example,dc=net
- # options:
- # filter: (&(objectClass=some-group-class)(!(groupType=email)))
-
locations:
# Add a location here to ingest it, for example from a URL:
#
@@ -325,15 +199,23 @@ catalog:
target: ../../plugins/scaffolder-backend/sample-templates/all-templates.yaml
rules:
- allow: [Template]
+
scaffolder:
+ auditor:
+ taskParameterMaxLength: 256
# Use to customize default commit author info used when new components are created
- # defaultAuthor:
- # name: Scaffolder
- # email: scaffolder@backstage.io
+ defaultAuthor:
+ name: Scaffolder
+ email: scaffolder@backstage.io
# Use to customize the default commit message when new components are created
- # defaultCommitMessage: 'Initial commit'
+ defaultCommitMessage: 'Initial commit'
auth:
+ experimentalDynamicClientRegistration:
+ enabled: true
+ allowedRedirectUriPatterns:
+ - cursor://*
+
### Add auth.keyStore.provider to more granularly control how to store JWK data when running
# the auth-backend.
#
@@ -422,80 +304,5 @@ auth:
myproxy: {}
guest: {}
-costInsights:
- engineerCost: 200000
- engineerThreshold: 0.5
- products:
- computeEngine:
- name: Compute Engine
- icon: compute
- cloudDataflow:
- name: Cloud Dataflow
- icon: data
- cloudStorage:
- name: Cloud Storage
- icon: storage
- bigQuery:
- name: BigQuery
- icon: search
- events:
- name: Events
- icon: data
- metrics:
- DAU:
- name: Daily Active Users
- default: true
- MSC:
- name: Monthly Subscribers
- currencies:
- engineers:
- label: 'Engineers 🛠'
- unit: 'engineer'
- usd:
- label: 'US Dollars 💵'
- kind: 'USD'
- unit: 'dollar'
- prefix: '$'
- rate: 1
- carbonOffsetTons:
- label: 'Carbon Offset Tons ♻️⚖️s'
- kind: 'CARBON_OFFSET_TONS'
- unit: 'carbon offset ton'
- rate: 3.5
- beers:
- label: 'Beers 🍺'
- kind: 'BEERS'
- unit: 'beer'
- rate: 4.5
- pintsIceCream:
- label: 'Pints of Ice Cream 🍦'
- kind: 'PINTS_OF_ICE_CREAM'
- unit: 'ice cream pint'
- rate: 5.5
-
-pagerDuty:
- eventsBaseUrl: 'https://events.pagerduty.com/v2'
-
-jenkins:
- instances:
- - name: default
- baseUrl: https://jenkins.example.com
- username: backstage-bot
- apiKey: 123456789abcdef0123456789abcedf012
-
-azureDevOps:
- host: dev.azure.com
- token: my-token
- organization: my-company
-
-apacheAirflow:
- baseUrl: https://your.airflow.instance.com
-
-gocd:
- baseUrl: https://your.gocd.instance.com
-
-stackstorm:
- webUrl: https://your.stackstorm.webui.instance.com
-
permission:
enabled: true
diff --git a/beps/0001-notifications-system/README.md b/beps/0001-notifications-system/README.md
index 91c1a81fe1..bfea3115f9 100644
--- a/beps/0001-notifications-system/README.md
+++ b/beps/0001-notifications-system/README.md
@@ -1,6 +1,6 @@
---
title: Backstage Notifications System
-status: implementable
+status: implemented
authors:
- '@Rugvip'
- '@drodil'
@@ -105,7 +105,6 @@ The notification backend stores notification using the [database service](https:
- Description (optional)
- Origin
- Link (optional)
- - Additional links (optional)
- Severity (optional, default normal)
- Topic (optional)
- Scope (optional)
@@ -193,8 +192,6 @@ The following backend service interfaces are added as part of this proposal.
#### `NotificationService`
```ts
-// TODO - We may want to add an additional wrapping here with interfaces for Notification and NotificationParameters
-
export type NotificationRecipients =
| {
type: 'entity';
@@ -210,15 +207,11 @@ export type NotificationPayload = {
title: string;
description?: string;
link?: string;
- additionalLinks?: string[];
severity?: NotificationSeverity;
topic?: string;
scope?: string;
icon?: string;
- metadata?: Array<{
- type: string;
- value: JsonValue;
- }>;
+ metadata?: { [KMetadataKey in string]?: JsonValue };
};
export type Notification = {
@@ -233,20 +226,20 @@ export type Notification = {
payload: NotificationPayload;
};
-interface SendNotificationRequest {
+interface NotificationSendOptions {
recipients: NotificationRecipients;
payload: NotificationPayload;
}
interface NotificationService {
- sendNotification(request: SendNotificationRequest): Promise;
+ send(request: NotificationSendOptions): Promise;
}
```
Each notification contains a human-readable `title`, `origin` and optionally `link` for additional details. The `created`, `id`, `read` and `saved` properties are handled by the backend based and cannot be passed during the notification creation.
Any optional additional details could be stored in `metadata`. We advise to provide the name to the type which contains the information about the context and the version, for example: 'core.icon.v1'.
-Calling `sendNotification` should never throw an error so that it doesn't block the current processing. Notifications should be considered as second-level citizens that are not critical if not delivered.
+Calling `send` should never throw an error so that it doesn't block the current processing. Notifications should be considered as second-level citizens that are not critical if not delivered.
Each notification is always routed to individual users unless its type is `broadcast`.
@@ -300,8 +293,6 @@ Example signal payload for a new notification:
- OpenAPI tooling is taken into use for the notification router and client
- Allow using dynamic values in notification payload, for example entity references `{{ user:default/john.doe }}` should be rendered by the frontend with `EntityRefLink` component. Defining the dynamic data values should be done before implementation.
-- Add configurable automatic clean-up of old notifications to save storage space
-- Support for saving user notification settings
### Frontend API
@@ -309,7 +300,7 @@ Notification frontend shows users their own notifications in its own page and th
Notifications are set to `read` when the `Mark as read` action is triggered by the user (bulk or single).
-Notifications can be saved for better visibility in the future.
+Notifications can be saved for better visibility.
Notifications can be filtered by `read`, `saved`, `content` (text search in title or description), `created` (since multiple predefined options) and `severity`.
@@ -322,42 +313,64 @@ The following frontend API is added as part of this proposal.
```ts
export type NotificationSeverity = 'critical' | 'high' | 'normal' | 'low';
-export type GetNotificationsOptions = {
- offset?: number;
- limit?: number;
+export type GetNotificationsCommonOptions = {
search?: string;
- createdSince?: Date;
- severity?: NotificationSeverity;
+ read?: boolean;
+ saved?: boolean;
+ createdAfter?: Date;
+ minimumSeverity?: NotificationSeverity;
};
-export type NotificationUpdateOptions = {
+export type GetNotificationsOptions = GetNotificationsCommonOptions & {
+ offset?: number;
+ limit?: number;
+ sort?: 'created' | 'topic' | 'origin';
+ sortOrder?: 'asc' | 'desc';
+ topic?: string;
+};
+
+export type GetTopicsOptions = GetNotificationsCommonOptions;
+
+export type UpdateNotificationsOptions = {
ids: string[];
read?: boolean;
saved?: boolean;
};
-export type NotificationStatus = {
- unread: number;
- read: number;
+export type GetNotificationsResponse = {
+ notifications: Notification[];
+ totalCount: number;
};
-interface NotificationsApi {
- getNotifications(options?: GetNotificationsOptions): Promise;
+export type GetTopicsResponse = {
+ topics: string[];
+};
+
+export interface NotificationsApi {
+ getNotifications(
+ options?: GetNotificationsOptions,
+ ): Promise;
+
+ getNotification(id: string): Promise;
getStatus(): Promise;
updateNotifications(
options: UpdateNotificationsOptions,
): Promise;
+
+ getNotificationSettings(): Promise;
+
+ updateNotificationSettings(
+ settings: NotificationSettings,
+ ): Promise;
+
+ getTopics(options?: GetTopicsOptions): Promise;
}
```
#### `SignalApi`
-> TODO - we likely need a slightly different approach here, since APIs are lazy loaded.
-
-> TODO - signal typing in https://github.com/backstage/backstage/pull/22656
-
```ts
export type NotificationSignal = {
action: string;
@@ -427,14 +440,14 @@ The notification and signal plugins are released as two new plugins in the Backs
For the notification plugin to reach a stable release we much reach the following:
-- [ ] A stable notifications payload format.
-- [ ] A stable notifications recipient filter format.
+- [x] A stable notifications payload format.
+- [x] A stable notifications recipient filter format.
- [x] The events service must have at least one implementation that supports scaled deployments. Done in #24916.
For the signal plugin to reach a stable release we much reach the following:
-- [ ] A stable signal recipient filter format.
-- [ ] A stable signal channel API in the frontend.
+- [x] A stable signal recipient filter format.
+- [x] A stable signal channel API in the frontend.
If any changes are required to the frontend framework to facilitate the implementation of notifications or signals, these will be released as experimental alpha features. They will stay in alpha until they are deemed stable enough, which must happen before a stable release of the notifications system.
diff --git a/beps/0012-metrics-service/README.md b/beps/0012-metrics-service/README.md
new file mode 100644
index 0000000000..59096d11d8
--- /dev/null
+++ b/beps/0012-metrics-service/README.md
@@ -0,0 +1,250 @@
+---
+title: Backstage Metrics Service
+status: implementable
+authors:
+ - '@kurtaking'
+owners:
+ - '@kurtaking'
+project-areas:
+ - core-framework
+creation-date: 2025-06-23
+---
+
+## Table of Contents
+
+- [Summary](#summary)
+- [Motivation](#motivation)
+ - [Goals](#goals)
+ - [Non-Goals](#non-goals)
+- [Proposal](#proposal)
+- [Naming Conventions](#naming-conventions)
+- [Design Details](#design-details)
+ - [References](#references)
+ - [Integration with OpenTelemetry Auto-Instrumentation](#integration-with-opentelemetry-auto-instrumentation)
+ - [Configuration](#configuration)
+ - [Interface](#interface)
+ - [Root Metrics Service](#root-metrics-service)
+ - [Plugin Metrics Service](#plugin-metrics-service)
+ - [Example](#example)
+- [Release Plan](#release-plan)
+- [Dependencies](#dependencies)
+- [Alternatives](#alternatives)
+
+## Summary
+
+Add a core `MetricsService` to Backstage's framework to provide a unified interface for metrics instrumentation. The service offers industry standards (OTEL) while focusing the `MetricsService` on distinct Backstage concerns, following the same pattern as other core services (DatabaseService builds on Knex, LoggerService builds on Winston, HttpRouterService builds on Express, etc.).
+
+## Motivation
+
+While individual plugins may implement their own metrics, there's no standardized approach leading to inconsistent metrics patterns across the ecosystem. For example, both `catalog_entities_count` and `catalog.processed.entities.count` are examples of existing metric patterns. Ideally, these would be standardized to `backstage.plugin.catalog.entities.count` and `backstage.plugin.catalog.entities.processed.total` respectively.
+
+By providing a core metrics service:
+
+- **Plugin Authors** and the **Community** gain a straightforward way to address metrics instrumentation and can focus on business logic instead of needing to reimplement metrics plumbing.
+- **Backstage Admins** receive a reliable stream of metrics from the core system for monitoring, alerting, and troubleshooting.
+
+### Goals
+
+- Plugin-scoped metric namespacing
+- Consistent metrics patterns across all plugins
+- Aligned with OpenTelemetry industry standards
+- Provide a familiar interface as other core services
+
+The catalog and scaffolder plugins will be updated to use the new metrics service in the initial alpha release.
+
+### Non-Goals
+
+- Providing a way to configure the OpenTelemetry SDK. This is out of scope for this BEP.
+- Adding metrics to plugins missing existing metrics (outside of catalog and scaffolder)
+- Tracing and other telemetry concerns are out of scope for this BEP.
+- Refactoring the existing `LoggerService`. Future work to unify observability related concerns would be ideal, but not a goal.
+
+## Proposal
+
+Following similar patterns to other core services, create a new `RootMetricsService` responsible for root-level concerns and the creation of plugin-specific `MetricsService` instances.
+
+## Naming Conventions
+
+All Backstage metrics follow this hierarchical pattern:
+
+`backstage.{scope}.{scope_name}.{metric_name}`
+
+**Where:**
+
+- `backstage` is the root namespace for all Backstage metrics
+- `{scope}` is the system scope (either **plugin** or **core**)
+- `{scope_name}` is the name of the plugin or core service (e.g., `catalog`, `scaffolder`, `database`, `scheduler`)
+- `{metric_name}` is the hierarchical metric name as provided by the plugin author (e.g., `entity.count`, `tasks.completed.total`)
+
+### Scope
+
+The `scope` represents where it belongs in the Backstage ecosystem.
+
+- `plugin` - A plugin-specific metric (e.g. `backstage.plugin.catalog.entity.count`)
+- `core` - A metric provided by the core system (e.g. `backstage.core.database.connections.active`)
+
+### Plugin-Scoped Metrics
+
+Pattern: `backstage.plugin.{pluginId}.{metric_name}`
+
+```yaml
+# Examples
+backstage.plugin.catalog.entities.processed.total
+backstage.plugin.scaffolder.tasks.completed.total
+backstage.plugin.techdocs.builds.active
+backstage.plugin.auth.sessions.active.total # todo: technically a core service and a backend plugin
+```
+
+### Core-Scoped Metrics
+
+Pattern: `backstage.core.{service}.{metric_name}`
+
+```yaml
+# Examples
+backstage.core.database.connections.active
+backstage.core.scheduler.tasks.queued.total
+backstage.core.httpRouter.requests.total
+```
+
+## Design Details
+
+### References
+
+- [General naming considerations](https://opentelemetry.io/docs/specs/semconv/general/naming/#general-naming-considerations)
+- [Acquiring a meter](https://opentelemetry.io/docs/languages/js/instrumentation/#acquiring-a-meter)
+
+### Integration with OpenTelemetry Auto-Instrumentation
+
+The `MetricsService` **complements** rather than duplicates auto-instrumentation by focusing on **application-level metrics** that only Backstage can provide. For example, the catalog plugin may want to track the number of entities processed by the `refresh` operation and the kind of entity being processed.
+
+```ts
+// Auto-instrumentation provides (automatically):
+// - http.server.requests.total{method="GET", route="/catalog/entities", status_code="200"}
+// - http.server.request.duration{method="GET", route="/catalog/entities"}
+
+// MetricsService provides (manually):
+const entityMetrics = metricsService.createCounter('entities.processed.total');
+entityMetrics.add(entities.length, { operation: 'refresh', kind: 'Component' });
+
+// Metric is now available as `backstage.plugin.catalog.entities.processed.total`
+```
+
+### Configuration
+
+A challenging factor of only introducing a `MetricsService` is the need to collect other OTEL-related configuration such as resources, tracing providers, views, and more prior to starting the SDK. This means that in order to introduce a `MetricsService`, we must support all OTEL Node SDK configuration along with it. Along with this, the official recommendation from the OTEL team is to not initialize and start the SDK on behalf of the user.
+
+With this, **we will not include any configuration** as part of this BEP. Users will be responsible for initializing the SDK based on the current guidance
+
+### Interface
+
+Provide a wrapper around OpenTelemetry's API while leveraging the types from the `@opentelemetry/api` package. This introduces concepts already familiar to both the Backstage community and those familiar with OpenTelemetry.
+
+```ts
+interface MetricsService {
+ // Synchronous instrumentation
+ createCounter(name: string, options?: MetricOptions): Counter;
+ createUpDownCounter(name: string, options?: MetricOptions): UpDownCounter;
+ createHistogram(name: string, options?: MetricOptions): Histogram;
+ createGauge(name: string, options?: MetricOptions): Gauge;
+
+ // Asynchronous instrumentation
+ createObservableCounter(
+ name: string,
+ options?: MetricOptions,
+ ): ObservableCounter;
+ createObservableUpDownCounter(
+ name: string,
+ options?: MetricOptions,
+ ): ObservableUpDownCounter;
+ createObservableGauge(name: string, options?: MetricOptions): ObservableGauge;
+
+ // Future - add additional convenience methods as we learn more about the needs of the framework
+}
+```
+
+#### Root Metrics Service
+
+The `RootMetricsService` is responsible for providing metrics to other root services and creating both plugin-scoped and core-scoped `MetricsService` instances.
+
+```ts
+interface RootMetricsService {
+ // note: no config is provided to the root service.
+ static forRoot(): RootMetricsService;
+ forPlugin(pluginId: string): MetricsService;
+
+ // final implementation will be similar to
+ forService(serviceName: string, scope: 'plugin' | 'core'): MetricsService;
+}
+
+export const rootMetricsServiceFactory = createServiceFactory({
+ // depends on as little as possible so that it can be initialized as early as possible.
+ service: rootMetricsServiceRef,
+ deps: {},
+ factory: () => {
+ return DefaultRootMetricsService.forRoot();
+ },
+});
+```
+
+#### Plugin Metrics Service
+
+Each plugin receives a metrics service that automatically namespaces all metrics to match the naming conventions.
+
+```ts
+const metricsServiceFactory = createServiceFactory({
+ service: metricsServiceRef,
+ deps: {
+ rootMetrics: coreServices.rootMetrics,
+ pluginMetadata: coreServices.pluginMetadata,
+ },
+ factory: ({ rootMetrics, pluginMetadata }) => {
+ return rootMetrics.forPlugin(pluginMetadata.getId());
+ },
+});
+```
+
+#### Example
+
+```ts
+const entitiesProcessed = metricsService.createCounter(
+ 'entities.processed.total',
+ {
+ description: 'Total entities processed during refresh',
+ unit: '{entity}',
+ },
+);
+
+entitiesProcessed.add(100);
+
+// ...
+// metric is now available as `backstage.plugin.catalog.entities.processed.total`
+```
+
+## Release Plan
+
+1. Create the new metrics-related services.
+1. Create alpha-related documentation to add to existing core service [docs](https://backstage.io/docs/backend-system/core-services/index).
+1. Release the metrics service under `@alpha`.
+1. Mark all existing metrics implementations as deprecated.
+1. Refactor catalog and scaffolder plugins to use the new (alpha) `MetricsService`.
+1. Offer a migration path for existing adopters to migrate to the new metrics service.
+1. Release the metrics service under `@public`
+1. Update remaining documentation to reference the new metrics service.
+1. Create follow-up action items to integrate the new metrics service into the core system.
+1. Fully deprecate all existing metrics implementations like the existing Prometheus one-off implementations.
+
+## Deprecation Plan
+
+1. Deprecation warning are added to all existing metrics
+2. New metrics will run in parallel with the deprecated ones for a period of time
+3. All existing metrics are removed from the codebase in the next major version
+
+## Dependencies
+
+1. The `otel` SDK MUST BE initialized as EARLY as possible to prevent dependents from receiving no-op meters - we will not change the current guidance on this
+1. There are one-off implementations of metrics in the wild that may conflict with the proposed service. However, this is unlikely to be a problem as the SDK should continue to pick things up.
+
+## Alternatives
+
+- Plugin authors continue to implement their own metrics as they see fit.
+- A combined TelemetryService that provides both metrics and tracing.
diff --git a/canon-docs/next.config.mjs b/canon-docs/next.config.mjs
deleted file mode 100644
index ca221d9a8c..0000000000
--- a/canon-docs/next.config.mjs
+++ /dev/null
@@ -1,15 +0,0 @@
-import createMDX from '@next/mdx';
-
-const nextConfig = {
- pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
- output: 'export',
- assetPrefix: '/',
- distDir: 'dist',
- images: {
- unoptimized: true,
- },
-};
-
-const withMDX = createMDX({});
-
-export default withMDX(nextConfig);
diff --git a/canon-docs/package.json b/canon-docs/package.json
deleted file mode 100644
index 5a4affc45b..0000000000
--- a/canon-docs/package.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "name": "canon-docs",
- "version": "0.1.0",
- "private": true,
- "scripts": {
- "build": "npm run build:css && next build",
- "build:css": "node scripts/build-css.js",
- "lint": "next lint",
- "start": "npm run build:css && next dev"
- },
- "dependencies": {
- "@codemirror/lang-sass": "^6.0.2",
- "@lezer/highlight": "^1.2.1",
- "@mdx-js/loader": "^3.1.0",
- "@mdx-js/react": "^3.1.0",
- "@next/mdx": "^15.1.4",
- "@storybook/react": "^8.6.8",
- "@uiw/codemirror-themes": "^4.23.7",
- "@uiw/react-codemirror": "^4.23.7",
- "motion": "^12.4.1",
- "next": "14.2.26",
- "react": "^18.0.2",
- "react-dom": "^18.0.2",
- "react-frame-component": "^5.2.7",
- "shiki": "^1.26.1",
- "storybook": "^8.6.8"
- },
- "devDependencies": {
- "@types/mdx": "^2.0.13",
- "@types/node": "^20",
- "@types/react": "^18.0.0",
- "@types/react-dom": "^18.0.0",
- "eslint": "^8",
- "eslint-config-next": "14.2.23",
- "lightningcss": "^1.28.2",
- "typescript": "^5"
- }
-}
diff --git a/canon-docs/public/CNAME b/canon-docs/public/CNAME
deleted file mode 100644
index 01f6d196cc..0000000000
--- a/canon-docs/public/CNAME
+++ /dev/null
@@ -1 +0,0 @@
-canon.backstage.io
\ No newline at end of file
diff --git a/canon-docs/public/css-classname-structure.png b/canon-docs/public/css-classname-structure.png
deleted file mode 100644
index df9894ff2b..0000000000
Binary files a/canon-docs/public/css-classname-structure.png and /dev/null differ
diff --git a/canon-docs/scripts/build-css.js b/canon-docs/scripts/build-css.js
deleted file mode 100644
index dd9775150f..0000000000
--- a/canon-docs/scripts/build-css.js
+++ /dev/null
@@ -1,39 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-const { bundle } = require('lightningcss');
-
-const source = '../../packages/canon/src/css';
-const destination = '../public';
-
-const source1 = path.join(__dirname, `${source}/core.css`);
-const destination1 = path.join(__dirname, `${destination}/core.css`);
-const source2 = path.join(__dirname, `${source}/components.css`);
-const destination2 = path.join(__dirname, `${destination}/components.css`);
-
-// Function to bundle and copy the CSS file
-const bundleAndCopyFile = async (source, destination) => {
- try {
- const result = await bundle({
- filename: source,
- minify: true,
- });
-
- fs.writeFileSync(destination, result.code);
- console.log('File bundled and copied successfully!');
- } catch (err) {
- console.error('Error bundling file:', err);
- }
-};
-
-// Initial bundle and copy
-Promise.all([
- bundleAndCopyFile(source1, destination1),
- bundleAndCopyFile(source2, destination2),
-])
- .then(() => {
- // Add an empty line after all operations are complete - It looks better in the terminal :)
- console.log('');
- })
- .catch(err => {
- console.error('Error in processing files:', err);
- });
diff --git a/canon-docs/src/app/(docs)/about/page.mdx b/canon-docs/src/app/(docs)/about/page.mdx
deleted file mode 100644
index a4c16ee79e..0000000000
--- a/canon-docs/src/app/(docs)/about/page.mdx
+++ /dev/null
@@ -1,32 +0,0 @@
-# About Canon
-
-Canon is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS.
-This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Canon
-is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users.
-
-## Philosophy
-
-Backstage empowers product teams to build software faster and with greater quality. Its extensibility,
-however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is
-to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while
-still allowing customization to match individual brands.
-
-Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless
-component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive
-and flexible theming system.
-
-## Team
-
-Canon is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in
-crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive
-user experiences for the music industry, we've created a design system that looks great and works seamlessly.
-
-## Community
-
-Canon is an open-source project and we welcome contributions from the community. If you are interested in
-contributing to Canon, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)
-and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md).
-
-## License
-
-Canon is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details.
diff --git a/canon-docs/src/app/(docs)/components/avatar/page.mdx b/canon-docs/src/app/(docs)/components/avatar/page.mdx
deleted file mode 100644
index b46caf8d62..0000000000
--- a/canon-docs/src/app/(docs)/components/avatar/page.mdx
+++ /dev/null
@@ -1,75 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import { AvatarSnippet } from '@/snippets/stories-snippets';
-import { avatarPropDefs } from './props';
-
-# Avatar
-
-An avatar component with a fallback for initials.
-
-}
- code={``}
-/>
-
-
-
- Usage
- Theming
-
-
- `}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- - `canon-AvatarRoot`
- - `canon-AvatarRoot[data-size='small']`
- - `canon-AvatarRoot[data-size='medium']`
- - `canon-AvatarRoot[data-size='large']`
- - `canon-AvatarImage`
- - `canon-AvatarFallback`
-
-
-
-## API reference
-
-
-
-## Examples
-
-### Sizes
-
-Avatar sizes can be set using the `size` prop.
-
-}
- code={`
-
-
-
- `}
-/>
-
-### Fallback
-
-If the image is not available, the avatar will show the initials of the name.
-
-}
- code={``}
-/>
diff --git a/canon-docs/src/app/(docs)/components/avatar/props.ts b/canon-docs/src/app/(docs)/components/avatar/props.ts
deleted file mode 100644
index e86c67a41e..0000000000
--- a/canon-docs/src/app/(docs)/components/avatar/props.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
-import type { PropDef } from '@/utils/propDefs';
-
-export const avatarPropDefs: Record = {
- src: {
- type: 'string',
- },
- name: {
- type: 'string',
- },
- size: {
- type: 'enum',
- values: ['small', 'medium', 'large'],
- default: 'medium',
- responsive: true,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/box/props.ts b/canon-docs/src/app/(docs)/components/box/props.ts
deleted file mode 100644
index f3e38984dd..0000000000
--- a/canon-docs/src/app/(docs)/components/box/props.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import {
- classNamePropDefs,
- displayPropDefs,
- heightPropDefs,
- positionPropDefs,
- stylePropDefs,
- widthPropDefs,
-} from '../../../../utils/propDefs';
-import type { PropDef } from '../../../../utils/propDefs';
-
-export const boxPropDefs: Record = {
- as: {
- type: 'enum',
- values: ['div', 'span'],
- default: 'div',
- responsive: true,
- },
- ...widthPropDefs,
- ...heightPropDefs,
- ...positionPropDefs,
- ...displayPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/button/page.mdx b/canon-docs/src/app/(docs)/components/button/page.mdx
deleted file mode 100644
index e1ecee8920..0000000000
--- a/canon-docs/src/app/(docs)/components/button/page.mdx
+++ /dev/null
@@ -1,136 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import { ButtonSnippet } from '@/snippets/stories-snippets';
-import { buttonVariants } from '@/snippets/code-snippets';
-import { buttonPropDefs } from './props';
-
-# Button
-
-A button component that can be used to trigger actions.
-
-}
- code={`
-
-
-
-`}
-/>
-
-
-
- Usage
- Theming
-
-
-
-`}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `}
- />
-
-
-
-
-## API reference
-
-
-
-## Examples
-
-### Variants
-
-Here's a view when buttons have different variants.
-
-}
- code={buttonVariants}
-/>
-
-### Sizes
-
-Here's a view when buttons have different sizes.
-
-}
- code={`
-
-
-`}
-/>
-
-### With Icons
-
-Here's a view when buttons have icons.
-
-}
- code={`
-
-
-
-`}
-/>
-
-### Full width
-
-Here's a view when buttons are full width.
-
-}
- code={`
-
-`}
-/>
-
-### Disabled
-
-Here's a view when buttons are disabled.
-
-}
- code={``}
-/>
-
-### Responsive
-
-Here's a view when buttons are responsive.
-
-
- Responsive Button
- `}
-/>
diff --git a/canon-docs/src/app/(docs)/components/button/props.ts b/canon-docs/src/app/(docs)/components/button/props.ts
deleted file mode 100644
index 813dcaa6f0..0000000000
--- a/canon-docs/src/app/(docs)/components/button/props.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
-import type { PropDef } from '../../../../utils/propDefs';
-
-export const buttonPropDefs: Record = {
- variant: {
- type: 'enum',
- values: ['primary', 'secondary'],
- default: 'primary',
- responsive: true,
- },
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'medium',
- responsive: true,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/checkbox/page.mdx b/canon-docs/src/app/(docs)/components/checkbox/page.mdx
deleted file mode 100644
index 413636dc9a..0000000000
--- a/canon-docs/src/app/(docs)/components/checkbox/page.mdx
+++ /dev/null
@@ -1,66 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { CheckboxSnippet } from '@/snippets/stories-snippets';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
-import { checkboxPropDefs } from './props';
-
-# Checkbox
-
-A checkbox component that can be used to trigger actions.
-
-}
- code={``}
-/>
-
-
-
- Usage
- Theming
-
-
-
-`}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `}
- />
-
-
-
-
-## API reference
-
-
-
-
-
-## Examples
-
-### All variants
-
-Here's a view when checkboxes have different variants.
-
-}
- code={`
-
-
-
-
- `}
-/>
diff --git a/canon-docs/src/app/(docs)/components/checkbox/props.ts b/canon-docs/src/app/(docs)/components/checkbox/props.ts
deleted file mode 100644
index 8abbac1ec9..0000000000
--- a/canon-docs/src/app/(docs)/components/checkbox/props.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
-import type { PropDef } from '../../../../utils/propDefs';
-
-export const checkboxPropDefs: Record = {
- label: {
- type: 'string',
- responsive: false,
- },
- defaultChecked: {
- type: 'enum',
- values: ['boolean', "'indeterminate'"],
- responsive: false,
- },
- checked: {
- type: 'enum',
- values: ['boolean', "'indeterminate'"],
- responsive: false,
- },
- onChange: {
- type: 'enum',
- values: ["(checked: boolean | 'indeterminate') => void"],
- responsive: false,
- },
- disabled: {
- type: 'enum',
- values: ['boolean'],
- responsive: false,
- },
- required: {
- type: 'enum',
- values: ['boolean'],
- responsive: false,
- },
- name: {
- type: 'string',
- responsive: false,
- },
- value: {
- type: 'string',
- responsive: false,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/collapsible/page.mdx b/canon-docs/src/app/(docs)/components/collapsible/page.mdx
deleted file mode 100644
index e013dff89c..0000000000
--- a/canon-docs/src/app/(docs)/components/collapsible/page.mdx
+++ /dev/null
@@ -1,113 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import { CollapsibleSnippet } from '@/snippets/stories-snippets';
-import {
- collapsibleRootPropDefs,
- collapsibleTriggerPropDefs,
- collapsiblePanelPropDefs,
-} from './props';
-
-# Collapsible
-
-An avatar component with a fallback for initials.
-
-}
- code={`
- (
-
- )} />
-
-
- It's the edge of the world and all of Western civilization
- The sun may rise in the East, at least it settled in a final location
- It's understood that Hollywood sells Californication
-
-
-`}
-/>
-
-
-
- Usage
- Theming
-
-
-
- (
-
- )} />
- Your content
-`}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- - `canon-CollapsibleRoot`
- - `canon-CollapsibleTrigger`
- - `canon-CollapsiblePanel`
-
-
-
-## API reference
-
-### Collapsible.Root
-
-Groups all parts of the collapsible. Renders a `
` element.
-
-
-
-### Collapsible.Trigger
-
-The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend
-using the `render` prop to render a custom trigger.
-
- } />`}
-/>
-
-
-
-### Collapsible.Panel
-
-A panel with the collapsible contents. Renders a `
` element.
-
-
-
-## Examples
-
-Open the panel by default by setting the `defaultOpen` prop to `true`.
-
-}
- code={`
- (
-
- )} />
-
-
- It's the edge of the world and all of Western civilization
- The sun may rise in the East, at least it settled in a final location
- It's understood that Hollywood sells Californication
-
-
-`}
-/>
diff --git a/canon-docs/src/app/(docs)/components/collapsible/props.ts b/canon-docs/src/app/(docs)/components/collapsible/props.ts
deleted file mode 100644
index 02c62dc89e..0000000000
--- a/canon-docs/src/app/(docs)/components/collapsible/props.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import {
- classNamePropDefs,
- stylePropDefs,
- renderPropDefs,
-} from '@/utils/propDefs';
-import type { PropDef } from '@/utils/propDefs';
-
-export const collapsibleRootPropDefs: Record = {
- defaultOpen: {
- type: 'boolean',
- default: 'false',
- },
- open: {
- type: 'boolean',
- },
- onOpenChange: {
- type: 'enum',
- values: ['(open) => void'],
- },
- ...renderPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const collapsibleTriggerPropDefs: Record = {
- ...renderPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const collapsiblePanelPropDefs: Record = {
- hiddenUntilFound: {
- type: 'boolean',
- default: 'false',
- },
- keepMounted: {
- type: 'boolean',
- default: 'false',
- },
- ...renderPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/container/page.mdx b/canon-docs/src/app/(docs)/components/container/page.mdx
deleted file mode 100644
index 40f0321624..0000000000
--- a/canon-docs/src/app/(docs)/components/container/page.mdx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { CodeBlock } from '@/components/CodeBlock';
-import { PropsTable } from '@/components/PropsTable';
-import { Tabs } from '@/components/Tabs';
-import { Snippet } from '@/components/Snippet';
-import { ContainerSnippet } from '@/snippets/stories-snippets';
-import { containerPropDefs } from './props';
-
-# Container
-
-The container component let you use our default max-width and center the
-content on the page.
-
-}
- code={`
-
-`}
-/>
-
-
-
- Usage
-
-
- Hello World!
-`} />
-
-
-
-## API reference
-
-
-
-## Examples
-
-### Simple
-
-A simple example of how to use the Container component.
-
-
- Hello World
- Hello World
- Hello World
-`}
-/>
-
-### Responsive padding & margin
-
-The Container component also supports responsive values, making it easy to
-create responsive designs.
-
-
- Hello World
- Hello World
- Hello World
-`}
-/>
diff --git a/canon-docs/src/app/(docs)/components/container/props.ts b/canon-docs/src/app/(docs)/components/container/props.ts
deleted file mode 100644
index 4a3410ec12..0000000000
--- a/canon-docs/src/app/(docs)/components/container/props.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import {
- classNamePropDefs,
- stylePropDefs,
- gapPropDefs,
-} from '../../../../utils/propDefs';
-import type { PropDef } from '../../../../utils/propDefs';
-
-export const containerPropDefs: Record = {
- ...gapPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/flex/page.mdx b/canon-docs/src/app/(docs)/components/flex/page.mdx
deleted file mode 100644
index 8150f3e3c5..0000000000
--- a/canon-docs/src/app/(docs)/components/flex/page.mdx
+++ /dev/null
@@ -1,83 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import {
- flexFAQ1,
- flexSimple,
- flexResponsive,
- flexAlign,
-} from '@/snippets/code-snippets';
-import { Snippet } from '@/components/Snippet';
-import { FlexSnippet } from '@/snippets/stories-snippets';
-import { flexPropDefs } from './props';
-import { spacingPropDefs } from '../../../../utils/propDefs';
-
-# Flex
-
-This is the flex container component. It will help to define the number of
-columns that will be used in the grid. You can also define the gap between the
-columns. All values are responsive.
-
-}
- code={`
-
-
-
-`}
-/>
-
-
-
- Usage
-
-
-
-`}
- />
-
-
-
-## API reference
-
-
-
-The grid component also accepts all the spacing props from the Box component.
-
-
-
-## Common questions
-
-### Can I stack horizontally?
-
-The Flex component only allows for stacking elements vertically. If you want
-to create a column layout, please use the Grid component.
-
-
-
-## Examples
-
-### Simple
-
-A simple example of how to use the Flex component.
-
-
-
-### Responsive
-
-The Flex component also supports responsive values, making it easy to create
-responsive designs.
-
-
-
-### Align
-
-The Flex component also supports responsive alignment, making it easy to
-create responsive designs.
-
-
diff --git a/canon-docs/src/app/(docs)/components/flex/props.ts b/canon-docs/src/app/(docs)/components/flex/props.ts
deleted file mode 100644
index 2268a6909d..0000000000
--- a/canon-docs/src/app/(docs)/components/flex/props.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import {
- PropDef,
- childrenPropDefs,
- classNamePropDefs,
- stylePropDefs,
-} from '../../../../utils/propDefs';
-
-export const flexPropDefs: Record = {
- align: {
- type: 'enum',
- values: ['start', 'center', 'end', 'baseline', 'stretch'],
- responsive: true,
- },
- direction: {
- type: 'enum',
- values: ['row', 'column', 'row-reverse', 'column-reverse'],
- responsive: true,
- },
- justify: {
- type: 'enum',
- values: ['start', 'center', 'end', 'between'],
- responsive: true,
- },
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/grid/props.ts b/canon-docs/src/app/(docs)/components/grid/props.ts
deleted file mode 100644
index a9e8717fa1..0000000000
--- a/canon-docs/src/app/(docs)/components/grid/props.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import {
- PropDef,
- childrenPropDefs,
- classNamePropDefs,
- stylePropDefs,
-} from '../../../../utils/propDefs';
-
-const columnsValues = [
- '1',
- '2',
- '3',
- '4',
- '5',
- '6',
- '7',
- '8',
- '9',
- '10',
- '11',
- '12',
-];
-
-export const gridPropDefs: Record = {
- columns: {
- type: 'enum | string',
- values: [...columnsValues, 'auto'],
- responsive: true,
- default: 'auto',
- },
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
-export const gridItemPropDefs: Record = {
- colSpan: {
- type: 'enum | string',
- values: [...columnsValues, 'full'],
- responsive: true,
- },
- rowSpan: {
- type: 'enum | string',
- values: [...columnsValues, 'full'],
- responsive: true,
- },
- start: {
- type: 'enum | string',
- values: [...columnsValues, 'auto'],
- responsive: true,
- },
- end: {
- type: 'enum | string',
- values: [...columnsValues, 'auto'],
- responsive: true,
- },
- ...childrenPropDefs,
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/heading/page.mdx b/canon-docs/src/app/(docs)/components/heading/page.mdx
deleted file mode 100644
index 10fd60bef1..0000000000
--- a/canon-docs/src/app/(docs)/components/heading/page.mdx
+++ /dev/null
@@ -1,74 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { HeadingSnippet } from '@/snippets/stories-snippets';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import { headingPropDefs } from './props';
-
-# Heading
-
-Headings are used to structure the content of your page.
-
-}
- code={`Hello World!`}
-/>
-
-
-
- Usage
- Theming
-
-
-
-`}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `}
- />
-
-
-
-
-## API reference
-
-
-
-## Examples
-
-### All variants
-
-The `Heading` component has a `variant` prop that can be used to change the
-appearance of the heading.
-
-}
- code={`
- Display
- Title 1
- Title 2
- Title 3
- Title 4
-`}
-/>
-
-### Responsive
-
-You can also use the `variant` prop to change the appearance of the text based
-on the screen size.
-
-
- Responsive heading
- `}
-/>
diff --git a/canon-docs/src/app/(docs)/components/heading/props.ts b/canon-docs/src/app/(docs)/components/heading/props.ts
deleted file mode 100644
index a3dd3288ff..0000000000
--- a/canon-docs/src/app/(docs)/components/heading/props.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
-import type { PropDef } from '../../../../utils/propDefs';
-
-export const headingPropDefs: Record = {
- variant: {
- type: 'enum',
- values: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'],
- responsive: true,
- },
- children: {
- type: 'enum',
- values: ['ReactNode'],
- responsive: false,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/icon-button/page.mdx b/canon-docs/src/app/(docs)/components/icon-button/page.mdx
deleted file mode 100644
index 60530ec9ea..0000000000
--- a/canon-docs/src/app/(docs)/components/icon-button/page.mdx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import { IconButtonSnippet } from '@/snippets/stories-snippets';
-import { iconButtonVariants } from '@/snippets/code-snippets';
-import { iconButtonPropDefs } from './props';
-
-# Icon Button
-
-A button component with a single icon that can be used to trigger actions.
-
-}
- code={`
-
-
- `}
-/>
-
-
-
- Usage
- Theming
-
-
-
-`}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `}
- />
-
-
-
-
-## API reference
-
-
-
-## Examples
-
-### Variants
-
-Here's a view when buttons have different variants.
-
-}
- code={iconButtonVariants}
-/>
-
-### Sizes
-
-Here's a view when buttons have different sizes.
-
-}
- code={`
-
-
-`}
-/>
-
-### Disabled
-
-Here's a view when buttons are disabled.
-
-}
- code={``}
-/>
-
-### Responsive
-
-Here's a view when buttons are responsive.
-
-`}
-/>
diff --git a/canon-docs/src/app/(docs)/components/icon-button/props.ts b/canon-docs/src/app/(docs)/components/icon-button/props.ts
deleted file mode 100644
index bcb1580247..0000000000
--- a/canon-docs/src/app/(docs)/components/icon-button/props.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
-import type { PropDef } from '../../../../utils/propDefs';
-
-export const iconButtonPropDefs: Record = {
- variant: {
- type: 'enum',
- values: ['primary', 'secondary'],
- default: 'primary',
- responsive: true,
- },
- size: {
- type: 'enum',
- values: ['small', 'medium'],
- default: 'medium',
- responsive: true,
- },
- icon: { type: 'enum', values: 'icon', responsive: false },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/icon/page.mdx b/canon-docs/src/app/(docs)/components/icon/page.mdx
deleted file mode 100644
index 66be460d13..0000000000
--- a/canon-docs/src/app/(docs)/components/icon/page.mdx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { IconSnippet } from '@/snippets/stories-snippets';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import { iconPropDefs } from './props';
-
-# Icon
-
-Icons are used to represent an action or a state.
-
-}
- code={``}
-/>
-
-
-
- Usage
- Theming
-
-
-
-`}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `}
- />
-
-
-
-
-## API reference
-
-
diff --git a/canon-docs/src/app/(docs)/components/icon/props.ts b/canon-docs/src/app/(docs)/components/icon/props.ts
deleted file mode 100644
index 2ddbafd94e..0000000000
--- a/canon-docs/src/app/(docs)/components/icon/props.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
-import type { PropDef } from '../../../../utils/propDefs';
-
-export const iconPropDefs: Record = {
- name: {
- type: 'enum',
- values: 'icon',
- responsive: false,
- },
- size: {
- type: 'number',
- responsive: false,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/link/page.mdx b/canon-docs/src/app/(docs)/components/link/page.mdx
deleted file mode 100644
index 64a3261590..0000000000
--- a/canon-docs/src/app/(docs)/components/link/page.mdx
+++ /dev/null
@@ -1,84 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import {
- MenuSnippet,
- ButtonSnippet,
- LinkSnippet,
-} from '@/snippets/stories-snippets';
-import { linkPropDefs } from './props';
-
-# Link
-
-A link component that renders a `` element.
-
-}
- code={`Sign up for Backstage`}
-/>
-
-
-
- Usage
- Theming
-
-
- Sign up for Backstage`}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- - `canon-Link`
- - `canon-Link[data-variant='body']`
- - `canon-Link[data-variant='subtitle']`
- - `canon-Link[data-variant='caption']`
- - `canon-Link[data-variant='label']`
- - `canon-Link[data-weight='regular']`
- - `canon-Link[data-weight='bold']`
-
-
-
-## API reference
-
-
-
-## Examples
-
-### Variants
-
-Here's a view when links have different variants.
-
-}
- code={`
-
-
-
-
- `}
-/>
-
-### Weights
-
-Here's a view when links have different weights.
-
-}
- code={`
-
-
- `}
-/>
diff --git a/canon-docs/src/app/(docs)/components/link/props.ts b/canon-docs/src/app/(docs)/components/link/props.ts
deleted file mode 100644
index 83507c5594..0000000000
--- a/canon-docs/src/app/(docs)/components/link/props.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
-import type { PropDef } from '@/utils/propDefs';
-
-export const linkPropDefs: Record = {
- to: {
- type: 'string',
- },
- variant: {
- type: 'enum',
- values: ['subtitle', 'body', 'caption', 'label'],
- default: 'body',
- responsive: true,
- },
- weight: {
- type: 'enum',
- values: ['regular', 'bold'],
- default: 'regular',
- responsive: true,
- },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
diff --git a/canon-docs/src/app/(docs)/components/menu/page.mdx b/canon-docs/src/app/(docs)/components/menu/page.mdx
deleted file mode 100644
index 98c4949438..0000000000
--- a/canon-docs/src/app/(docs)/components/menu/page.mdx
+++ /dev/null
@@ -1,157 +0,0 @@
-import { PropsTable } from '@/components/PropsTable';
-import { Snippet } from '@/components/Snippet';
-import { Tabs } from '@/components/Tabs';
-import { CodeBlock } from '@/components/CodeBlock';
-import { MenuSnippet } from '@/snippets/stories-snippets';
-import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
-import {
- menuRootPropDefs,
- menuTriggerPropDefs,
- menuPositionerPropDefs,
- menuItemPropDefs,
-} from './props';
-
-# Menu
-
-A list of actions in a dropdown, enhanced with keyboard navigation.
-
-}
- code={`
- (
-
- )}
- />
-
-
-
- Settings
- Invite new members
- Download app
- Log out
-
-
-
- `}
-/>
-
-
-
- Usage
- Theming
-
-
-
-
-
-
-
-
-
-
-
- `}
- />
-
-
- We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
- your brand. For additional flexibility, you can use the provided class names for each element listed below.
- - `canon-MenuTrigger`
- - `canon-MenuBackdrop`
- - `canon-MenuPositioner`
- - `canon-MenuPopup`
- - `canon-MenuArrow`
- - `canon-MenuItem`
- - `canon-MenuGroup`
- - `canon-MenuGroupLabel`
- - `canon-MenuRadioGroup`
- - `canon-MenuRadioItem`
- - `canon-MenuRadioItemIndicator`
- - `canon-MenuCheckboxItem`
- - `canon-MenuCheckboxItemIndicator`
- - `canon-MenuSubmenuTrigger`
- - `canon-MenuSeparator`
-
-
-
-## API reference
-
-
-
-### Menu.Root
-
-Groups all parts of the menu. Doesn't render its own HTML element.
-
-
-
-### Menu.Trigger
-
-The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend
-using the `render` prop to render a custom trigger.
-
- } />`} />
-
-
-
-### Menu.Positioner
-
-Positions the menu popup against the trigger. Renders a `
- ),
- pre: ({ children }) => {
- const codeContent = isValidElement(children)
- ? (children.props as { children: string }).children
- : '';
-
- return ;
- },
- code: ({ children }) => (
-
- {children}
-
- ),
- img: props => (
-
- ),
- ...components,
- };
-}
diff --git a/canon-docs/src/snippets/code-snippets.ts b/canon-docs/src/snippets/code-snippets.ts
deleted file mode 100644
index 00a9c1462d..0000000000
--- a/canon-docs/src/snippets/code-snippets.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-// Sometimes codes are not formatted correctly in the docs, so we need to use snippets
-
-export const customTheme = `:root {
- --canon-font-regular: system-ui;
- --canon-font-weight-regular: 400;
- --canon-font-weight-bold: 600;
- --canon-bg: #f8f8f8;
- --canon-bg-surface-1: #fff;
- /* ... other CSS variables */
-
- /* Add your custom components styles here */
- .canon-Button {
- background-color: #000;
- color: #fff;
- }
-}
-
-[data-theme='dark'] {
- --canon-font-regular: system-ui;
- --canon-font-weight-regular: 400;
- --canon-font-weight-bold: 600;
- --canon-bg: #f8f8f8;
- --canon-bg-surface-1: #fff;
- /* ... other CSS variables */
-
- /* Add your custom components styles here */
- .canon-Button {
- background-color: #000;
- color: #fff;
- }
-}
-`;
-
-export const grid = `import { Grid } from '@backstage/canon';
-
-
-
-
-`;
-
-export const buttonVariants = `
-
-
-
-
-`;
-
-export const iconButtonVariants = `
-
-
-
-`;
-
-export const flexFAQ1 = `
- Hello World
- Hello World
- Hello World
-`;
-
-export const flexSimple = `
- Hello World
- Hello World
- Hello World
-`;
-
-export const flexResponsive = `
- Hello World
- Hello World
- Hello World
-`;
-
-export const flexAlign = `
- Hello World
- Hello World
- Hello World
-`;
diff --git a/canon-docs/src/snippets/stories-snippets.tsx b/canon-docs/src/snippets/stories-snippets.tsx
deleted file mode 100644
index 75ee2e31c3..0000000000
--- a/canon-docs/src/snippets/stories-snippets.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-'use client';
-
-import { composeStories } from '@storybook/react';
-import * as BoxStories from '../../../packages/canon/src/components/Box/Box.stories';
-import * as ButtonStories from '../../../packages/canon/src/components/Button/Button.stories';
-import * as CheckboxStories from '../../../packages/canon/src/components/Checkbox/Checkbox.stories';
-import * as ContainerStories from '../../../packages/canon/src/components/Container/Container.stories';
-import * as GridStories from '../../../packages/canon/src/components/Grid/Grid.stories';
-import * as HeadingStories from '../../../packages/canon/src/components/Heading/Heading.stories';
-import * as IconButtonStories from '../../../packages/canon/src/components/IconButton/IconButton.stories';
-import * as IconStories from '../../../packages/canon/src/components/Icon/Icon.stories';
-import * as TextFieldStories from '../../../packages/canon/src/components/TextField/TextField.stories';
-import * as TextStories from '../../../packages/canon/src/components/Text/Text.stories';
-import * as FlexStories from '../../../packages/canon/src/components/Flex/Flex.stories';
-import * as SelectStories from '../../../packages/canon/src/components/Select/Select.stories';
-import * as MenuStories from '../../../packages/canon/src/components/Menu/Menu.stories';
-import * as LinkStories from '../../../packages/canon/src/components/Link/Link.stories';
-import * as AvatarStories from '../../../packages/canon/src/components/Avatar/Avatar.stories';
-import * as CollapsibleStories from '../../../packages/canon/src/components/Collapsible/Collapsible.stories';
-
-export const BoxSnippet = ({ story }: { story: keyof typeof BoxStories }) => {
- const stories = composeStories(BoxStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const ButtonSnippet = ({
- story,
-}: {
- story: keyof typeof ButtonStories;
-}) => {
- const stories = composeStories(ButtonStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const CheckboxSnippet = ({
- story,
-}: {
- story: keyof typeof CheckboxStories;
-}) => {
- const stories = composeStories(CheckboxStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const ContainerSnippet = ({
- story,
-}: {
- story: keyof typeof ContainerStories;
-}) => {
- const stories = composeStories(ContainerStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const FlexSnippet = ({ story }: { story: keyof typeof FlexStories }) => {
- const stories = composeStories(FlexStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const TextFieldSnippet = ({
- story,
-}: {
- story: keyof typeof TextFieldStories;
-}) => {
- const stories = composeStories(TextFieldStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const GridSnippet = ({ story }: { story: keyof typeof GridStories }) => {
- const stories = composeStories(GridStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const HeadingSnippet = ({
- story,
-}: {
- story: keyof typeof HeadingStories;
-}) => {
- const stories = composeStories(HeadingStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const IconButtonSnippet = ({
- story,
-}: {
- story: keyof typeof IconButtonStories;
-}) => {
- const stories = composeStories(IconButtonStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const IconSnippet = ({ story }: { story: keyof typeof IconStories }) => {
- const stories = composeStories(IconStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const TextSnippet = ({ story }: { story: keyof typeof TextStories }) => {
- const stories = composeStories(TextStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const SelectSnippet = ({
- story,
-}: {
- story: keyof typeof SelectStories;
-}) => {
- const stories = composeStories(SelectStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const MenuSnippet = ({ story }: { story: keyof typeof MenuStories }) => {
- const stories = composeStories(MenuStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const LinkSnippet = ({ story }: { story: keyof typeof LinkStories }) => {
- const stories = composeStories(LinkStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const AvatarSnippet = ({
- story,
-}: {
- story: keyof typeof AvatarStories;
-}) => {
- const stories = composeStories(AvatarStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
-
-export const CollapsibleSnippet = ({
- story,
-}: {
- story: keyof typeof CollapsibleStories;
-}) => {
- const stories = composeStories(CollapsibleStories);
- const StoryComponent = stories[story as keyof typeof stories];
-
- return StoryComponent ? : null;
-};
diff --git a/canon-docs/src/utils/data.ts b/canon-docs/src/utils/data.ts
deleted file mode 100644
index 891b057ad6..0000000000
--- a/canon-docs/src/utils/data.ts
+++ /dev/null
@@ -1,147 +0,0 @@
-interface Page {
- title: string;
- slug: string;
- status?: 'alpha' | 'beta' | 'stable' | 'deprecated' | 'inProgress';
-}
-
-export const overview: Page[] = [
- {
- title: 'Getting Started',
- slug: '',
- },
- {
- title: 'About',
- slug: 'about',
- },
- {
- title: 'Releases',
- slug: 'releases',
- },
-];
-
-export const theme: Page[] = [
- {
- title: 'Iconography',
- slug: 'iconography',
- },
- {
- title: 'Layout',
- slug: 'layout',
- },
- {
- title: 'Responsive',
- slug: 'responsive',
- },
- {
- title: 'Theming',
- slug: 'theming',
- },
- {
- title: 'Typography',
- slug: 'typography',
- },
-];
-
-export const layoutComponents: Page[] = [
- {
- title: 'Box',
- slug: 'box',
- status: 'alpha',
- },
- {
- title: 'Container',
- slug: 'container',
- status: 'alpha',
- },
- {
- title: 'Grid',
- slug: 'grid',
- status: 'alpha',
- },
- {
- title: 'Flex',
- slug: 'flex',
- status: 'alpha',
- },
-];
-
-export const components: Page[] = [
- {
- title: 'Avatar',
- slug: 'avatar',
- status: 'alpha',
- },
- {
- title: 'Button',
- slug: 'button',
- status: 'alpha',
- },
- {
- title: 'Checkbox',
- slug: 'checkbox',
- status: 'alpha',
- },
- {
- title: 'Collapsible',
- slug: 'collapsible',
- status: 'alpha',
- },
- {
- title: 'Heading',
- slug: 'heading',
- status: 'alpha',
- },
- {
- title: 'Icon',
- slug: 'icon',
- status: 'alpha',
- },
- {
- title: 'IconButton',
- slug: 'icon-button',
- status: 'alpha',
- },
- {
- title: 'Link',
- slug: 'link',
- status: 'alpha',
- },
- {
- title: 'Menu',
- slug: 'menu',
- status: 'alpha',
- },
- {
- title: 'Select',
- slug: 'select',
- status: 'alpha',
- },
- {
- title: 'Table',
- slug: 'table',
- status: 'inProgress',
- },
- {
- title: 'Text',
- slug: 'text',
- status: 'alpha',
- },
- {
- title: 'TextField',
- slug: 'text-field',
- status: 'alpha',
- },
-];
-
-export type ScreenSize = {
- title: string;
- slug: string;
- width: number;
-};
-
-export const screenSizes: ScreenSize[] = [
- { title: 'Mobile', slug: 'mobile', width: 390 },
- { title: 'Tablet', slug: 'tablet', width: 768 },
- { title: 'Desktop', slug: 'desktop', width: 1280 },
- { title: 'Wide', slug: 'wide', width: 1600 },
-];
diff --git a/contrib/catalog/read-write-split/README.md b/contrib/catalog/read-write-split/README.md
new file mode 100644
index 0000000000..4a99ebe076
--- /dev/null
+++ b/contrib/catalog/read-write-split/README.md
@@ -0,0 +1,85 @@
+# Catalog Read-Write Split
+
+This article gives an overview of how you might split your catalog deployment into two - one which handles only reads (the read part of the API), and another that handles everything else (the write part of the API, and processing and ingestion etc).
+
+This is useful for several reasons:
+
+- The performance characteristics and scaling needs of those two concerns typically differ significantly
+- Moving the bursty processing and ingestion load on a separate deployment can make the important read part of the API have more deterministic response times
+- It opens up for easier cross region deployment of read nodes, while having write nodes in a single "home" region
+- It also opens up for primary/secondary split of the underlying database, with streaming replication
+
+## Overview
+
+This is a rough overview of what we will go through:
+
+- Ensure that all frontend code uses `catalogApiRef`, and all backend code uses `catalogServiceRef`, for talking to the catalog
+- Add a separate discovery name for the write deployment
+- Register both frontend and backend implementations of the catalog client that know to send traffic to the right discovery name as needed
+- Set up the backend itself with dedicated read and write configuration
+- Split the deployment into two
+- Follow up with a database split, if desired
+
+## 1. Prerequisite: Establish usage of catalog client refs
+
+First, ensure that all of your code uses _injected_ catalog clients for its catalog communications needs instead of making their own with `new`. For frontend code that means leveraging `catalogApiRef`, and for backend it means leveraging `catalogServiceRef`. This is what enables the sort of wholesale injection of custom behaviors that we are about to do, and is a strongly recommended best practice either way.
+
+- ✅ Search through your projects and ensure that there are no `new CatalogClient` calls. If you find any, consider rewriting that code to instead having the client injected through leveraging the corresponding ref.
+
+- ✅ Search through your projects and ensure that you do _not_ import the `catalogServiceRef` that specifically is in the `@backstage/plugin-catalog-node/alpha` export. That one has been deprecated for a long time and will not be targeted by this guide. If you find any, consider rewriting that code to instead inject the client from `@backstage/plugin-catalog-node` (note the lack of `/alpha`).
+
+> [!TIP]
+> If you find usages of `new CatalogClient` in tests, you can normally leave those as-is. You can however consider to later try out the `catalogApiMock` from `@backstage/plugin-catalog-react/testUtils` for frontend tests, and `catalogServiceMock` from `@backstage/plugin-catalog-node/testUtils` for backend tests. These allow you to inject a client that behaves like a complete fake catalog backend with entities in it, without complex mocking.
+
+## 2. Decide your deployment split structure
+
+We will be defining a fake plugin ID called `catalog-write`. In addition to your current single catalog deployment you will add a second one that receives traffic for `catalog-write`. You need to decide upon what your ingress to those two deployments will look like. This is highly company dependent, so we cannot give general guidance.
+
+This guide will however NOT split the code base into two. It will instead change your catalog backend code in such a way that it can support either deployment. This is all very flexible and you can tweak it as you see fit, but that's what worked well for us.
+
+This guide will assume that when all is said and done, requests for the `catalog` plugin ID resolve to the URL `https://catalog-read.example.net/api/catalog`, and requests for the `catalog-write` plugin ID resolve to the URL `https://catalog-write.example.net/api/catalog`. This is just for illustration and you shall change these to their actual correct values at your company.
+
+- ✅ Copy the contents of [the `app-config.yaml` file](./workspace/app-config.yaml) to your own. We will assume that these exact settings are applied both to your frontend and your catalog (read AND write) deployment. For now, if you are applying this guide step by step rather than all at once, you can set the URLs in there to point to your good old pre-split deployment and only update them again after the split.
+
+> [!TIP]
+> As a future refactor, you could instead break out this behavior into a custom injected discovery implementation in a library that all your projects can import. That takes a few more steps and is left as a separate exercise. If you already have such a library in your internal NPM, feel free to put it there to begin with and import from there.
+
+## 3. Get split-aware catalog clients in place
+
+Now let's put clients in place whose methods send traffic to either `catalog` or `catalog-write` as needed.
+
+- ✅ Copy [the `catalogApi.ts` file](./workspace/packages/app/src/catalogApi.ts) into your `packages/app/src` directory of the project where you develop your Backstage frontend. Then reference that file's API factory in your `packages/app/src/apis.ts` file, [as illustrated here](./workspace/packages/app/src/apis.ts).
+
+- ✅ Copy [the `catalogService.ts` file](./workspace/packages/backend/src/catalogService.ts) into your `packages/backend/src` directory of the project where you develop your Software Catalog backend. We will reference the file itself in the step below.
+
+After step 4 below is complete, these will be automatically picked up and used by all callers.
+
+> [!TIP]
+> As a future refactor, you could instead break out these clients into a node and a react library that all your projects can import. That takes a few more steps and is left as a separate exercise. If you already have such libraries in your internal NPM, feel free to put them there to begin with and import from there.
+
+## 4. Split the catalog backend
+
+We'll now perform the actual split of the catalog backend. You will make separate config files that apply to each, and adapt the backend code to handle both cases seamlessly.
+
+- ✅ Duplicate your deployment. It's highly company dependent how this is done. Some may for example duplicate some declarative `Deployment` and `Service` k8s manifests etc, but it depends.
+
+- ✅ Copy [the `app-config.catalog-read.yaml`] and [the `app-config.catalog-write.yaml`] files into your project root. You must set up your read and write deployments to load their respective files with the `--config` flag, _in addition to_ the `app-config.yaml` file and possibly `app-config.production.yaml` that they already loaded.
+
+- ✅ Copy [the `config.d.ts`](./workspace/config.d.ts) file to your catalog backend directory at `packages/backend`. You should refer to it in the `package.json`'s `"files"` and `"configSchema"` fields [as per usual](https://backstage.io/docs/conf/defining).
+
+- ✅ Update your `packages/backend/src/index.ts` file to match [the proposed structure here](./workspace/packages/backend/src/index.ts). Note how it applies the custom catalog service we copied in the previous step, and has a separate section for write-only concerns.
+
+## 5. Region and database split
+
+After the above is deployed and working, you are well set up for some next step.
+
+- Scaling your readers across multiple regions and add geo-aware routing to them, while keeping writers in a "home" region
+- Splitting the database server into a primary that the write nodes connect to, and regional secondaries with streaming replication from the primary
+
+But all of this is left as an exercise for the reader.
+
+## 6. Help make this smoother
+
+Congratulations on successfully performing your split!
+
+We would love [some help](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) with making this process easier for others. For example, if read nodes could automatically transparently proxy traffic to write nodes as needed, we would need far less fiddling with clients at a relatively small extra cost.
diff --git a/contrib/catalog/read-write-split/workspace/app-config.catalog-read.yaml b/contrib/catalog/read-write-split/workspace/app-config.catalog-read.yaml
new file mode 100644
index 0000000000..33d7f34714
--- /dev/null
+++ b/contrib/catalog/read-write-split/workspace/app-config.catalog-read.yaml
@@ -0,0 +1,12 @@
+# This file should be given to the catalog read nodes.
+
+backend:
+ database:
+ # disable running database migrations for every plugin/module
+ skipMigrations: true
+
+catalog:
+ role: read
+
+ # disable processing engine
+ processingInterval: false
diff --git a/contrib/catalog/read-write-split/workspace/app-config.catalog-write.yaml b/contrib/catalog/read-write-split/workspace/app-config.catalog-write.yaml
new file mode 100644
index 0000000000..ea1659e140
--- /dev/null
+++ b/contrib/catalog/read-write-split/workspace/app-config.catalog-write.yaml
@@ -0,0 +1,4 @@
+# This file should be given to the catalog write nodes.
+
+catalog:
+ role: write
diff --git a/contrib/catalog/read-write-split/workspace/app-config.yaml b/contrib/catalog/read-write-split/workspace/app-config.yaml
new file mode 100644
index 0000000000..e4b09e4cb9
--- /dev/null
+++ b/contrib/catalog/read-write-split/workspace/app-config.yaml
@@ -0,0 +1,19 @@
+# This is in your regular app-config.
+
+# This introduces a made-up "catalog-write" plugin ID that the frontend catalog
+# API implementation can use to resolve where to find the write nodes.
+discovery:
+ endpoints:
+ #
+ # TODO: Replace with actual URLs
+ #
+ # NOTE: Each target can also be an object with "internal" and "external"
+ # keys with different URLs, if they should be different when called by
+ # backends and the frontend, respectively.
+ #
+ - target: https://catalog-read.example.net/api/catalog
+ plugins:
+ - catalog
+ - target: https://catalog-write.example.net/api/catalog
+ plugins:
+ - catalog-write
diff --git a/contrib/catalog/read-write-split/workspace/config.d.ts b/contrib/catalog/read-write-split/workspace/config.d.ts
new file mode 100644
index 0000000000..29a80c77f8
--- /dev/null
+++ b/contrib/catalog/read-write-split/workspace/config.d.ts
@@ -0,0 +1,5 @@
+export interface Config {
+ catalog: {
+ role: 'read' | 'write';
+ };
+}
diff --git a/contrib/catalog/read-write-split/workspace/packages/app/src/apis.ts b/contrib/catalog/read-write-split/workspace/packages/app/src/apis.ts
new file mode 100644
index 0000000000..12fd2bfd49
--- /dev/null
+++ b/contrib/catalog/read-write-split/workspace/packages/app/src/apis.ts
@@ -0,0 +1,11 @@
+import { AnyApiFactory } from '@backstage/core-plugin-api';
+import { catalogApiFactory } from './catalogApi';
+
+// This file is already present in a typical Backstage app. We are just adding
+// the catalog API factory here to ensure that the frontend sends requests to
+// the correct set of catalog nodes depending on whether it's a read or a write
+// operation.
+export const apis: AnyApiFactory[] = [
+ // ...other APIs here...
+ catalogApiFactory,
+];
diff --git a/contrib/catalog/read-write-split/workspace/packages/app/src/catalogApi.ts b/contrib/catalog/read-write-split/workspace/packages/app/src/catalogApi.ts
new file mode 100644
index 0000000000..4035e8d336
--- /dev/null
+++ b/contrib/catalog/read-write-split/workspace/packages/app/src/catalogApi.ts
@@ -0,0 +1,185 @@
+import {
+ AddLocationRequest,
+ AddLocationResponse,
+ CatalogApi,
+ CatalogClient,
+ CatalogRequestOptions,
+ GetEntitiesByRefsRequest,
+ GetEntitiesByRefsResponse,
+ GetEntitiesRequest,
+ GetEntitiesResponse,
+ GetEntityAncestorsRequest,
+ GetEntityAncestorsResponse,
+ GetEntityFacetsRequest,
+ GetEntityFacetsResponse,
+ GetLocationsResponse,
+ Location,
+ QueryEntitiesRequest,
+ QueryEntitiesResponse,
+ ValidateEntityResponse,
+} from '@backstage/catalog-client';
+import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
+import {
+ createApiFactory,
+ DiscoveryApi,
+ discoveryApiRef,
+ FetchApi,
+ fetchApiRef,
+} from '@backstage/core-plugin-api';
+import { catalogApiRef } from '@backstage/plugin-catalog-react';
+import {
+ AnalyzeLocationRequest,
+ AnalyzeLocationResponse,
+} from '@backstage/plugin-catalog-common';
+
+/**
+ * Implements the catalog client for frontends, by using a custom service
+ * discovery to achieve read/write segregation.
+ */
+export class ReadWriteSplitCatalogClient implements CatalogApi {
+ #write: CatalogApi;
+ #read: CatalogApi;
+
+ constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {
+ this.#read = new CatalogClient({
+ fetchApi: options.fetchApi,
+ discoveryApi: {
+ // This uses the normal "catalog" plugin ID
+ getBaseUrl: () => options.discoveryApi.getBaseUrl('catalog'),
+ },
+ });
+ this.#write = new CatalogClient({
+ fetchApi: options.fetchApi,
+ discoveryApi: {
+ // This uses the made-up "catalog-write" plugin ID (see app-config.yaml
+ // which defines a custom discovery endpoint for this)
+ getBaseUrl: () => options.discoveryApi.getBaseUrl('catalog-write'),
+ },
+ });
+ }
+
+ getEntities(
+ request?: GetEntitiesRequest,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getEntities(request, options);
+ }
+
+ getEntitiesByRefs(
+ request: GetEntitiesByRefsRequest,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getEntitiesByRefs(request, options);
+ }
+
+ queryEntities(
+ request?: QueryEntitiesRequest,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.queryEntities(request, options);
+ }
+
+ getEntityAncestors(
+ request: GetEntityAncestorsRequest,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getEntityAncestors(request, options);
+ }
+
+ getEntityByRef(
+ entityRef: string | CompoundEntityRef,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getEntityByRef(entityRef, options);
+ }
+
+ removeEntityByUid(
+ uid: string,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#write.removeEntityByUid(uid, options);
+ }
+
+ refreshEntity(
+ entityRef: string,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#write.refreshEntity(entityRef, options);
+ }
+
+ getEntityFacets(
+ request: GetEntityFacetsRequest,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getEntityFacets(request, options);
+ }
+
+ getLocations(
+ request?: {},
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getLocations(request, options);
+ }
+
+ getLocationById(
+ id: string,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getLocationById(id, options);
+ }
+
+ getLocationByRef(
+ locationRef: string,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getLocationByRef(locationRef, options);
+ }
+
+ addLocation(
+ location: AddLocationRequest,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#write.addLocation(location, options);
+ }
+
+ removeLocationById(
+ id: string,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#write.removeLocationById(id, options);
+ }
+
+ getLocationByEntity(
+ entityRef: string | CompoundEntityRef,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#read.getLocationByEntity(entityRef, options);
+ }
+
+ validateEntity(
+ entity: Entity,
+ locationRef: string,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#write.validateEntity(entity, locationRef, options);
+ }
+
+ analyzeLocation(
+ location: AnalyzeLocationRequest,
+ options?: CatalogRequestOptions,
+ ): Promise {
+ return this.#write.analyzeLocation(location, options);
+ }
+}
+
+// You should pass this to createApp.
+export const catalogApiFactory = createApiFactory({
+ api: catalogApiRef,
+ deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },
+ factory: ({ discoveryApi, fetchApi }) => {
+ return new ReadWriteSplitCatalogClient({
+ discoveryApi,
+ fetchApi,
+ });
+ },
+});
diff --git a/contrib/catalog/read-write-split/workspace/packages/backend/src/catalogService.ts b/contrib/catalog/read-write-split/workspace/packages/backend/src/catalogService.ts
new file mode 100644
index 0000000000..44119947e6
--- /dev/null
+++ b/contrib/catalog/read-write-split/workspace/packages/backend/src/catalogService.ts
@@ -0,0 +1,245 @@
+import {
+ AuthService,
+ coreServices,
+ createServiceFactory,
+ DiscoveryService,
+} from '@backstage/backend-plugin-api';
+import {
+ AddLocationRequest,
+ AddLocationResponse,
+ CatalogApi,
+ CatalogClient,
+ CatalogRequestOptions,
+ GetEntitiesByRefsRequest,
+ GetEntitiesByRefsResponse,
+ GetEntitiesRequest,
+ GetEntitiesResponse,
+ GetEntityAncestorsRequest,
+ GetEntityAncestorsResponse,
+ GetEntityFacetsRequest,
+ GetEntityFacetsResponse,
+ GetLocationsResponse,
+ Location,
+ QueryEntitiesRequest,
+ QueryEntitiesResponse,
+ ValidateEntityResponse,
+} from '@backstage/catalog-client';
+import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
+import {
+ AnalyzeLocationRequest,
+ AnalyzeLocationResponse,
+} from '@backstage/plugin-catalog-common';
+import {
+ CatalogService,
+ CatalogServiceRequestOptions,
+ catalogServiceRef,
+} from '@backstage/plugin-catalog-node';
+
+/**
+ * Implements the catalog client for backends, by using a custom service
+ * discovery to achieve read/write segregation.
+ */
+export class ReadWriteSplitCatalogService implements CatalogService {
+ readonly #auth: AuthService;
+ readonly #catalogRead: CatalogApi;
+ readonly #catalogWrite: CatalogApi;
+
+ constructor(options: { auth: AuthService; discovery: DiscoveryService }) {
+ this.#auth = options.auth;
+ this.#catalogRead = new CatalogClient({
+ discoveryApi: {
+ // This uses the normal "catalog" plugin ID
+ getBaseUrl: () => options.discovery.getBaseUrl('catalog'),
+ },
+ });
+ this.#catalogWrite = new CatalogClient({
+ discoveryApi: {
+ // This uses the made-up "catalog-write" plugin ID (see app-config.yaml
+ // which defines a custom discovery endpoint for this)
+ getBaseUrl: () => options.discovery.getBaseUrl('catalog-write'),
+ },
+ });
+ }
+
+ async getEntities(
+ request: GetEntitiesRequest | undefined,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getEntities(
+ request,
+ await this.#getOptions(options),
+ );
+ }
+
+ async getEntitiesByRefs(
+ request: GetEntitiesByRefsRequest,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getEntitiesByRefs(
+ request,
+ await this.#getOptions(options),
+ );
+ }
+
+ async queryEntities(
+ request: QueryEntitiesRequest | undefined,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.queryEntities(
+ request,
+ await this.#getOptions(options),
+ );
+ }
+
+ async getEntityAncestors(
+ request: GetEntityAncestorsRequest,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getEntityAncestors(
+ request,
+ await this.#getOptions(options),
+ );
+ }
+
+ async getEntityByRef(
+ entityRef: string | CompoundEntityRef,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getEntityByRef(
+ entityRef,
+ await this.#getOptions(options),
+ );
+ }
+
+ async removeEntityByUid(
+ uid: string,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogWrite.removeEntityByUid(
+ uid,
+ await this.#getOptions(options),
+ );
+ }
+
+ async refreshEntity(
+ entityRef: string,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogWrite.refreshEntity(
+ entityRef,
+ await this.#getOptions(options),
+ );
+ }
+
+ async getEntityFacets(
+ request: GetEntityFacetsRequest,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getEntityFacets(
+ request,
+ await this.#getOptions(options),
+ );
+ }
+
+ async getLocations(
+ options: CatalogRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getLocations(options);
+ }
+
+ async getLocationById(
+ id: string,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getLocationById(
+ id,
+ await this.#getOptions(options),
+ );
+ }
+
+ async getLocationByRef(
+ locationRef: string,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getLocationByRef(
+ locationRef,
+ await this.#getOptions(options),
+ );
+ }
+
+ async addLocation(
+ location: AddLocationRequest,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogWrite.addLocation(
+ location,
+ await this.#getOptions(options),
+ );
+ }
+
+ async removeLocationById(
+ id: string,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogWrite.removeLocationById(
+ id,
+ await this.#getOptions(options),
+ );
+ }
+
+ async getLocationByEntity(
+ entityRef: string | CompoundEntityRef,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogRead.getLocationByEntity(
+ entityRef,
+ await this.#getOptions(options),
+ );
+ }
+
+ async validateEntity(
+ entity: Entity,
+ locationRef: string,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogWrite.validateEntity(
+ entity,
+ locationRef,
+ await this.#getOptions(options),
+ );
+ }
+
+ async analyzeLocation(
+ location: AnalyzeLocationRequest,
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#catalogWrite.analyzeLocation(
+ location,
+ await this.#getOptions(options),
+ );
+ }
+
+ async #getOptions(
+ options: CatalogServiceRequestOptions,
+ ): Promise {
+ return this.#auth.getPluginRequestToken({
+ onBehalfOf: options.credentials,
+ targetPluginId: 'catalog',
+ });
+ }
+}
+
+// You should register this in your backend.
+export const catalogService = createServiceFactory({
+ service: catalogServiceRef,
+ deps: {
+ auth: coreServices.auth,
+ discovery: coreServices.discovery,
+ },
+ async factory({ auth, discovery }) {
+ return new ReadWriteSplitCatalogService({
+ auth,
+ discovery,
+ });
+ },
+});
diff --git a/contrib/catalog/read-write-split/workspace/packages/backend/src/index.ts b/contrib/catalog/read-write-split/workspace/packages/backend/src/index.ts
new file mode 100644
index 0000000000..0d8e9e41d9
--- /dev/null
+++ b/contrib/catalog/read-write-split/workspace/packages/backend/src/index.ts
@@ -0,0 +1,34 @@
+import { createBackend } from '@backstage/backend-defaults';
+import {
+ coreServices,
+ createBackendFeatureLoader,
+} from '@backstage/backend-plugin-api';
+import { catalogService } from './catalogService';
+
+const backend = createBackend();
+
+backend.add(import('@backstage/plugin-catalog-backend'));
+backend.add(catalogService);
+// Here at the root level, you can add more features that you want to apply to
+// BOTH the read and write nodes, as needed.
+
+backend.add(
+ createBackendFeatureLoader({
+ deps: { config: coreServices.rootConfig },
+ async *loader({ config }) {
+ if (config.getString('catalog.role') !== 'write') {
+ return;
+ }
+
+ // Here inside the loader, you can add more features that you want to
+ // apply to ONLY the write nodes, as needed. This is where providers and
+ // processors go, for example. The incremental ingestion provider line
+ // below is just an example.
+ yield import(
+ '@backstage/plugin-catalog-backend-module-incremental-ingestion'
+ );
+ },
+ }),
+);
+
+backend.start();
diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
index 47fd506a34..6c87681391 100644
--- a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
+++ b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild
@@ -6,7 +6,7 @@
# This dockerfile also performs the build first inside docker. This may come
# with a build time impact, but is sometimes desirable. If you want to run the
-# build on the host instead, use the file simply named Dockerfile in this folder
+# build on the host instead, use the file simply named Dockerfile.hostbuild in this folder
# instead.
# USAGE:
diff --git a/docker-compose.deps.yml b/docker-compose.deps.yml
new file mode 100644
index 0000000000..c7515eca04
--- /dev/null
+++ b/docker-compose.deps.yml
@@ -0,0 +1,39 @@
+#!/usr/bin/env docker compose -f
+name: backstage
+services:
+ psql:
+ image: postgres:17.6
+ environment:
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ ports:
+ - '5432:5432'
+ healthcheck:
+ test: ['CMD-SHELL', 'pg_isready']
+ interval: 10s
+ retries: 5
+ start_period: 30s
+ timeout: 10s
+ redis:
+ image: redis:8.2.1-alpine
+ ports:
+ - '6379:6379'
+ healthcheck:
+ test: ['CMD-SHELL', 'redis-cli ping | grep PONG']
+ interval: 1s
+ timeout: 3s
+ retries: 5
+ opensearch:
+ image: opensearchproject/opensearch:2.19.3
+ environment:
+ plugins.security.disabled: true
+ discovery.type: single-node
+ OPENSEARCH_INITIAL_ADMIN_PASSWORD: Opensearch1!
+ ports:
+ - '9200:9200'
+ - '9600:9600'
+ healthcheck:
+ test: 'curl --fail opensearch:9200/_cat/health >/dev/null || exit 1'
+ interval: 5s
+ timeout: 5s
+ retries: 30
diff --git a/canon-docs/.eslintrc.json b/docs-ui/.eslintrc.json
similarity index 100%
rename from canon-docs/.eslintrc.json
rename to docs-ui/.eslintrc.json
diff --git a/canon-docs/.gitignore b/docs-ui/.gitignore
similarity index 100%
rename from canon-docs/.gitignore
rename to docs-ui/.gitignore
diff --git a/canon-docs/README.md b/docs-ui/README.md
similarity index 60%
rename from canon-docs/README.md
rename to docs-ui/README.md
index d91076c4cd..41b9921413 100644
--- a/canon-docs/README.md
+++ b/docs-ui/README.md
@@ -1,6 +1,6 @@
-# Canon Docs
+# Backstage UI Docs
-Canon is our internal UI library built for Backstage. We built this website to document the library and its components. You can view this website [here](https://canon.backstage.io).
+Backstage UI is our internal UI library built for Backstage. We built this website to document the library and its components. You can view this website [here](https://ui.backstage.io).
## How to run locally
diff --git a/docs-ui/next.config.mjs b/docs-ui/next.config.mjs
new file mode 100644
index 0000000000..2ff341b609
--- /dev/null
+++ b/docs-ui/next.config.mjs
@@ -0,0 +1,26 @@
+import createMDX from '@next/mdx';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const nextConfig = {
+ pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
+ output: 'export',
+ assetPrefix: '/',
+ distDir: 'dist',
+ images: {
+ unoptimized: true,
+ },
+ typescript: {
+ // Ignore TypeScript errors during build - safe for React 18/19 compatibility issues
+ // These are type-level conflicts that don't affect runtime behavior
+ ignoreBuildErrors: true,
+ },
+ outputFileTracingRoot: path.join(
+ path.dirname(fileURLToPath(import.meta.url)),
+ '..',
+ ),
+};
+
+const withMDX = createMDX({});
+
+export default withMDX(nextConfig);
diff --git a/docs-ui/package.json b/docs-ui/package.json
new file mode 100644
index 0000000000..75b79d1000
--- /dev/null
+++ b/docs-ui/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "docs-ui",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "prebuild": "yarn sync:css",
+ "build": "next build",
+ "lint": "next lint",
+ "prestart": "yarn sync:css",
+ "start": "next dev",
+ "sync:css": "node scripts/sync-css.js",
+ "sync:css:watch": "node scripts/sync-css.js --watch"
+ },
+ "resolutions": {
+ "@types/react": "19.1.9",
+ "@types/react-dom": "19.1.7"
+ },
+ "dependencies": {
+ "@codemirror/lang-sass": "^6.0.2",
+ "@codemirror/view": "^6.34.4",
+ "@lezer/highlight": "^1.2.1",
+ "@mdx-js/loader": "^3.1.0",
+ "@mdx-js/react": "^3.1.0",
+ "@next/mdx": "15.3.4",
+ "@remixicon/react": "^4.6.0",
+ "@storybook/react": "^8.6.12",
+ "@uiw/codemirror-themes": "^4.23.7",
+ "@uiw/react-codemirror": "^4.23.7",
+ "clsx": "^2.1.1",
+ "html-react-parser": "^5.2.5",
+ "motion": "^12.4.1",
+ "next": "15.4.7",
+ "next-mdx-remote-client": "^2.1.2",
+ "prop-types": "^15.8.1",
+ "react": "19.1.1",
+ "react-dom": "19.1.1",
+ "shiki": "^3.13.0",
+ "storybook": "^8.6.12"
+ },
+ "devDependencies": {
+ "@shikijs/transformers": "^3.13.0",
+ "@types/mdx": "^2.0.13",
+ "@types/node": "^20",
+ "@types/react": "19.1.9",
+ "@types/react-dom": "19.1.7",
+ "chokidar": "^3.6.0",
+ "eslint": "^8",
+ "eslint-config-next": "15.3.4",
+ "lightningcss": "^1.28.2",
+ "typescript": "^5"
+ }
+}
diff --git a/canon-docs/public/.nojekyll b/docs-ui/public/.nojekyll
similarity index 100%
rename from canon-docs/public/.nojekyll
rename to docs-ui/public/.nojekyll
diff --git a/docs-ui/public/CNAME b/docs-ui/public/CNAME
new file mode 100644
index 0000000000..21060e506a
--- /dev/null
+++ b/docs-ui/public/CNAME
@@ -0,0 +1 @@
+ui.backstage.io
diff --git a/docs-ui/public/css-classname-structure.png b/docs-ui/public/css-classname-structure.png
new file mode 100644
index 0000000000..c241b182cc
Binary files /dev/null and b/docs-ui/public/css-classname-structure.png differ
diff --git a/docs-ui/public/theme-backstage.css b/docs-ui/public/theme-backstage.css
new file mode 100644
index 0000000000..59a5b986be
--- /dev/null
+++ b/docs-ui/public/theme-backstage.css
@@ -0,0 +1 @@
+@layer tokens{:root{--bui-font-regular:system-ui;--bui-font-monospace:ui-monospace,"Menlo","Monaco","Consolas","Liberation Mono","Courier New",monospace;--bui-font-weight-regular:400;--bui-font-weight-bold:600;--bui-font-size-1:.625rem;--bui-font-size-2:.75rem;--bui-font-size-3:.875rem;--bui-font-size-4:1rem;--bui-font-size-5:1.25rem;--bui-font-size-6:1.5rem;--bui-font-size-7:2rem;--bui-font-size-8:3rem;--bui-font-size-9:4rem;--bui-font-size-10:5.75rem;--bui-space:.25rem;--bui-space-0_5:calc(var(--bui-space)*.5);--bui-space-1:var(--bui-space);--bui-space-1_5:calc(var(--bui-space)*1.5);--bui-space-2:calc(var(--bui-space)*2);--bui-space-3:calc(var(--bui-space)*3);--bui-space-4:calc(var(--bui-space)*4);--bui-space-5:calc(var(--bui-space)*5);--bui-space-6:calc(var(--bui-space)*6);--bui-space-7:calc(var(--bui-space)*7);--bui-space-8:calc(var(--bui-space)*8);--bui-space-9:calc(var(--bui-space)*9);--bui-space-10:calc(var(--bui-space)*10);--bui-space-11:calc(var(--bui-space)*11);--bui-space-12:calc(var(--bui-space)*12);--bui-space-13:calc(var(--bui-space)*13);--bui-space-14:calc(var(--bui-space)*14);--bui-radius-1:calc(.125rem);--bui-radius-2:calc(.25rem);--bui-radius-3:calc(.5rem);--bui-radius-4:calc(.75rem);--bui-radius-5:calc(1rem);--bui-radius-6:calc(1.25rem);--bui-radius-full:9999px;--bui-black:#000;--bui-white:#fff;--bui-gray-1:#f8f8f8;--bui-gray-2:#ececec;--bui-gray-3:#d9d9d9;--bui-gray-4:#c1c1c1;--bui-gray-5:#9e9e9e;--bui-gray-6:#8c8c8c;--bui-gray-7:#757575;--bui-gray-8:#595959;--bui-bg:var(--bui-gray-1);--bui-bg-surface-1:var(--bui-white);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#1f5493;--bui-bg-solid-hover:#163a66;--bui-bg-solid-pressed:#0f2b4e;--bui-bg-solid-disabled:#ebebeb;--bui-bg-tint:transparent;--bui-bg-tint-hover:#1f549366;--bui-bg-tint-pressed:#1f549399;--bui-bg-tint-disabled:#ebebeb;--bui-bg-danger:#feebe7;--bui-bg-warning:#fff2b2;--bui-bg-success:#e6f6eb;--bui-fg-primary:var(--bui-black);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:#1f5493;--bui-fg-link-hover:#1f2d5c;--bui-fg-disabled:#9e9e9e;--bui-fg-solid:var(--bui-white);--bui-fg-solid-disabled:#9c9c9c;--bui-fg-tint:#1f5493;--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:#0000001a;--bui-border-hover:#0003;--bui-border-pressed:#0006;--bui-border-disabled:#0000001a;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#1f5493;--bui-scrollbar:#a0a0a03b;--bui-scrollbar-thumb:#a0a0a0;--bui-animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite}[data-theme-mode=dark]{--bui-gray-1:#191919;--bui-gray-2:#242424;--bui-gray-3:#373737;--bui-gray-4:#464646;--bui-gray-5:#575757;--bui-gray-6:#7b7b7b;--bui-gray-7:#9e9e9e;--bui-gray-8:#b4b4b4;--bui-bg:#333;--bui-bg-surface-1:#424242;--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#9cc9ff;--bui-bg-solid-hover:#83b9fd;--bui-bg-solid-pressed:#83b9fd;--bui-bg-solid-disabled:#222;--bui-bg-tint:transparent;--bui-bg-tint-hover:#9cc9ff1f;--bui-bg-tint-pressed:#9cc9ff29;--bui-bg-tint-disabled:transparent;--bui-bg-danger:#3b1219;--bui-bg-warning:#302008;--bui-bg-success:#132d21;--bui-fg-primary:var(--bui-white);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:#9cc9ff;--bui-fg-link-hover:#7eb5f7;--bui-fg-disabled:var(--bui-gray-7);--bui-fg-solid:#101821;--bui-fg-solid-disabled:var(--bui-gray-5);--bui-fg-tint:#9cc9ff;--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:#ffffff1f;--bui-border-hover:#fff6;--bui-border-pressed:#ffffff80;--bui-border-disabled:#fff3;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#1f5493;--bui-scrollbar:#3636363a;--bui-scrollbar-thumb:#575757}}@layer base{*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;tab-size:4;font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;line-height:1.15}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentColor}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}:where(a){color:inherit;text-decoration:none}@keyframes pulse{50%{opacity:.5}}body{background-color:var(--bui-bg);color:var(--bui-fg-primary);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);font-size:var(--bui-font-size-3);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}}@layer components;@layer utilities{.bui-p{padding:var(--p)}.bui-p-0\.5{padding:var(--bui-space-0_5)}.bui-p-1{padding:var(--bui-space-1)}.bui-p-1\.5{padding:var(--bui-space-1_5)}.bui-p-2{padding:var(--bui-space-2)}.bui-p-3{padding:var(--bui-space-3)}.bui-p-4{padding:var(--bui-space-4)}.bui-p-5{padding:var(--bui-space-5)}.bui-p-6{padding:var(--bui-space-6)}.bui-p-7{padding:var(--bui-space-7)}.bui-p-8{padding:var(--bui-space-8)}.bui-p-9{padding:var(--bui-space-9)}.bui-p-10{padding:var(--bui-space-10)}.bui-p-11{padding:var(--bui-space-11)}.bui-p-12{padding:var(--bui-space-12)}.bui-p-13{padding:var(--bui-space-13)}.bui-p-14{padding:var(--bui-space-14)}@media (width>=640px){.xs\:bui-p{padding:var(--p-xs)}.xs\:bui-p-0\.5{padding:var(--bui-space-0_5)}.xs\:bui-p-1{padding:var(--bui-space-1)}.xs\:bui-p-1\.5{padding:var(--bui-space-1_5)}.xs\:bui-p-2{padding:var(--bui-space-2)}.xs\:bui-p-3{padding:var(--bui-space-3)}.xs\:bui-p-4{padding:var(--bui-space-4)}.xs\:bui-p-5{padding:var(--bui-space-5)}.xs\:bui-p-6{padding:var(--bui-space-6)}.xs\:bui-p-7{padding:var(--bui-space-7)}.xs\:bui-p-8{padding:var(--bui-space-8)}.xs\:bui-p-9{padding:var(--bui-space-9)}.xs\:bui-p-10{padding:var(--bui-space-10)}.xs\:bui-p-11{padding:var(--bui-space-11)}.xs\:bui-p-12{padding:var(--bui-space-12)}.xs\:bui-p-13{padding:var(--bui-space-13)}.xs\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-p{padding:var(--p-sm)}.sm\:bui-p-0\.5{padding:var(--bui-space-0_5)}.sm\:bui-p-1{padding:var(--bui-space-1)}.sm\:bui-p-1\.5{padding:var(--bui-space-1_5)}.sm\:bui-p-2{padding:var(--bui-space-2)}.sm\:bui-p-3{padding:var(--bui-space-3)}.sm\:bui-p-4{padding:var(--bui-space-4)}.sm\:bui-p-5{padding:var(--bui-space-5)}.sm\:bui-p-6{padding:var(--bui-space-6)}.sm\:bui-p-7{padding:var(--bui-space-7)}.sm\:bui-p-8{padding:var(--bui-space-8)}.sm\:bui-p-9{padding:var(--bui-space-9)}.sm\:bui-p-10{padding:var(--bui-space-10)}.sm\:bui-p-11{padding:var(--bui-space-11)}.sm\:bui-p-12{padding:var(--bui-space-12)}.sm\:bui-p-13{padding:var(--bui-space-13)}.sm\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-p{padding:var(--p-md)}.md\:bui-p-0\.5{padding:var(--bui-space-0_5)}.md\:bui-p-1{padding:var(--bui-space-1)}.md\:bui-p-1\.5{padding:var(--bui-space-1_5)}.md\:bui-p-2{padding:var(--bui-space-2)}.md\:bui-p-3{padding:var(--bui-space-3)}.md\:bui-p-4{padding:var(--bui-space-4)}.md\:bui-p-5{padding:var(--bui-space-5)}.md\:bui-p-6{padding:var(--bui-space-6)}.md\:bui-p-7{padding:var(--bui-space-7)}.md\:bui-p-8{padding:var(--bui-space-8)}.md\:bui-p-9{padding:var(--bui-space-9)}.md\:bui-p-10{padding:var(--bui-space-10)}.md\:bui-p-11{padding:var(--bui-space-11)}.md\:bui-p-12{padding:var(--bui-space-12)}.md\:bui-p-13{padding:var(--bui-space-13)}.md\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-p{padding:var(--p-lg)}.lg\:bui-p-0\.5{padding:var(--bui-space-0_5)}.lg\:bui-p-1{padding:var(--bui-space-1)}.lg\:bui-p-1\.5{padding:var(--bui-space-1_5)}.lg\:bui-p-2{padding:var(--bui-space-2)}.lg\:bui-p-3{padding:var(--bui-space-3)}.lg\:bui-p-4{padding:var(--bui-space-4)}.lg\:bui-p-5{padding:var(--bui-space-5)}.lg\:bui-p-6{padding:var(--bui-space-6)}.lg\:bui-p-7{padding:var(--bui-space-7)}.lg\:bui-p-8{padding:var(--bui-space-8)}.lg\:bui-p-9{padding:var(--bui-space-9)}.lg\:bui-p-10{padding:var(--bui-space-10)}.lg\:bui-p-11{padding:var(--bui-space-11)}.lg\:bui-p-12{padding:var(--bui-space-12)}.lg\:bui-p-13{padding:var(--bui-space-13)}.lg\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-p{padding:var(--p-xl)}.xl\:bui-p-0\.5{padding:var(--bui-space-0_5)}.xl\:bui-p-1{padding:var(--bui-space-1)}.xl\:bui-p-1\.5{padding:var(--bui-space-1_5)}.xl\:bui-p-2{padding:var(--bui-space-2)}.xl\:bui-p-3{padding:var(--bui-space-3)}.xl\:bui-p-4{padding:var(--bui-space-4)}.xl\:bui-p-5{padding:var(--bui-space-5)}.xl\:bui-p-6{padding:var(--bui-space-6)}.xl\:bui-p-7{padding:var(--bui-space-7)}.xl\:bui-p-8{padding:var(--bui-space-8)}.xl\:bui-p-9{padding:var(--bui-space-9)}.xl\:bui-p-10{padding:var(--bui-space-10)}.xl\:bui-p-11{padding:var(--bui-space-11)}.xl\:bui-p-12{padding:var(--bui-space-12)}.xl\:bui-p-13{padding:var(--bui-space-13)}.xl\:bui-p-14{padding:var(--bui-space-14)}}.bui-pl{padding-left:var(--pl)}.bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.bui-pl-1{padding-left:var(--bui-space-1)}.bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.bui-pl-2{padding-left:var(--bui-space-2)}.bui-pl-3{padding-left:var(--bui-space-3)}.bui-pl-4{padding-left:var(--bui-space-4)}.bui-pl-5{padding-left:var(--bui-space-5)}.bui-pl-6{padding-left:var(--bui-space-6)}.bui-pl-7{padding-left:var(--bui-space-7)}.bui-pl-8{padding-left:var(--bui-space-8)}.bui-pl-9{padding-left:var(--bui-space-9)}.bui-pl-10{padding-left:var(--bui-space-10)}.bui-pl-11{padding-left:var(--bui-space-11)}.bui-pl-12{padding-left:var(--bui-space-12)}.bui-pl-13{padding-left:var(--bui-space-13)}.bui-pl-14{padding-left:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pl{padding-left:var(--pl-xs)}.xs\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.xs\:bui-pl-1{padding-left:var(--bui-space-1)}.xs\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.xs\:bui-pl-2{padding-left:var(--bui-space-2)}.xs\:bui-pl-3{padding-left:var(--bui-space-3)}.xs\:bui-pl-4{padding-left:var(--bui-space-4)}.xs\:bui-pl-5{padding-left:var(--bui-space-5)}.xs\:bui-pl-6{padding-left:var(--bui-space-6)}.xs\:bui-pl-7{padding-left:var(--bui-space-7)}.xs\:bui-pl-8{padding-left:var(--bui-space-8)}.xs\:bui-pl-9{padding-left:var(--bui-space-9)}.xs\:bui-pl-10{padding-left:var(--bui-space-10)}.xs\:bui-pl-11{padding-left:var(--bui-space-11)}.xs\:bui-pl-12{padding-left:var(--bui-space-12)}.xs\:bui-pl-13{padding-left:var(--bui-space-13)}.xs\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pl{padding-left:var(--pl-sm)}.sm\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.sm\:bui-pl-1{padding-left:var(--bui-space-1)}.sm\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.sm\:bui-pl-2{padding-left:var(--bui-space-2)}.sm\:bui-pl-3{padding-left:var(--bui-space-3)}.sm\:bui-pl-4{padding-left:var(--bui-space-4)}.sm\:bui-pl-5{padding-left:var(--bui-space-5)}.sm\:bui-pl-6{padding-left:var(--bui-space-6)}.sm\:bui-pl-7{padding-left:var(--bui-space-7)}.sm\:bui-pl-8{padding-left:var(--bui-space-8)}.sm\:bui-pl-9{padding-left:var(--bui-space-9)}.sm\:bui-pl-10{padding-left:var(--bui-space-10)}.sm\:bui-pl-11{padding-left:var(--bui-space-11)}.sm\:bui-pl-12{padding-left:var(--bui-space-12)}.sm\:bui-pl-13{padding-left:var(--bui-space-13)}.sm\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pl{padding-left:var(--pl-md)}.md\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.md\:bui-pl-1{padding-left:var(--bui-space-1)}.md\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.md\:bui-pl-2{padding-left:var(--bui-space-2)}.md\:bui-pl-3{padding-left:var(--bui-space-3)}.md\:bui-pl-4{padding-left:var(--bui-space-4)}.md\:bui-pl-5{padding-left:var(--bui-space-5)}.md\:bui-pl-6{padding-left:var(--bui-space-6)}.md\:bui-pl-7{padding-left:var(--bui-space-7)}.md\:bui-pl-8{padding-left:var(--bui-space-8)}.md\:bui-pl-9{padding-left:var(--bui-space-9)}.md\:bui-pl-10{padding-left:var(--bui-space-10)}.md\:bui-pl-11{padding-left:var(--bui-space-11)}.md\:bui-pl-12{padding-left:var(--bui-space-12)}.md\:bui-pl-13{padding-left:var(--bui-space-13)}.md\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pl{padding-left:var(--pl-lg)}.lg\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.lg\:bui-pl-1{padding-left:var(--bui-space-1)}.lg\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.lg\:bui-pl-2{padding-left:var(--bui-space-2)}.lg\:bui-pl-3{padding-left:var(--bui-space-3)}.lg\:bui-pl-4{padding-left:var(--bui-space-4)}.lg\:bui-pl-5{padding-left:var(--bui-space-5)}.lg\:bui-pl-6{padding-left:var(--bui-space-6)}.lg\:bui-pl-7{padding-left:var(--bui-space-7)}.lg\:bui-pl-8{padding-left:var(--bui-space-8)}.lg\:bui-pl-9{padding-left:var(--bui-space-9)}.lg\:bui-pl-10{padding-left:var(--bui-space-10)}.lg\:bui-pl-11{padding-left:var(--bui-space-11)}.lg\:bui-pl-12{padding-left:var(--bui-space-12)}.lg\:bui-pl-13{padding-left:var(--bui-space-13)}.lg\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pl{padding-left:var(--pl-xl)}.xl\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.xl\:bui-pl-1{padding-left:var(--bui-space-1)}.xl\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.xl\:bui-pl-2{padding-left:var(--bui-space-2)}.xl\:bui-pl-3{padding-left:var(--bui-space-3)}.xl\:bui-pl-4{padding-left:var(--bui-space-4)}.xl\:bui-pl-5{padding-left:var(--bui-space-5)}.xl\:bui-pl-6{padding-left:var(--bui-space-6)}.xl\:bui-pl-7{padding-left:var(--bui-space-7)}.xl\:bui-pl-8{padding-left:var(--bui-space-8)}.xl\:bui-pl-9{padding-left:var(--bui-space-9)}.xl\:bui-pl-10{padding-left:var(--bui-space-10)}.xl\:bui-pl-11{padding-left:var(--bui-space-11)}.xl\:bui-pl-12{padding-left:var(--bui-space-12)}.xl\:bui-pl-13{padding-left:var(--bui-space-13)}.xl\:bui-pl-14{padding-left:var(--bui-space-14)}}.bui-pr{padding-right:var(--pr)}.bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.bui-pr-1{padding-right:var(--bui-space-1)}.bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.bui-pr-2{padding-right:var(--bui-space-2)}.bui-pr-3{padding-right:var(--bui-space-3)}.bui-pr-4{padding-right:var(--bui-space-4)}.bui-pr-5{padding-right:var(--bui-space-5)}.bui-pr-6{padding-right:var(--bui-space-6)}.bui-pr-7{padding-right:var(--bui-space-7)}.bui-pr-8{padding-right:var(--bui-space-8)}.bui-pr-9{padding-right:var(--bui-space-9)}.bui-pr-10{padding-right:var(--bui-space-10)}.bui-pr-11{padding-right:var(--bui-space-11)}.bui-pr-12{padding-right:var(--bui-space-12)}.bui-pr-13{padding-right:var(--bui-space-13)}.bui-pr-14{padding-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pr{padding-right:var(--pr-xs)}.xs\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.xs\:bui-pr-1{padding-right:var(--bui-space-1)}.xs\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.xs\:bui-pr-2{padding-right:var(--bui-space-2)}.xs\:bui-pr-3{padding-right:var(--bui-space-3)}.xs\:bui-pr-4{padding-right:var(--bui-space-4)}.xs\:bui-pr-5{padding-right:var(--bui-space-5)}.xs\:bui-pr-6{padding-right:var(--bui-space-6)}.xs\:bui-pr-7{padding-right:var(--bui-space-7)}.xs\:bui-pr-8{padding-right:var(--bui-space-8)}.xs\:bui-pr-9{padding-right:var(--bui-space-9)}.xs\:bui-pr-10{padding-right:var(--bui-space-10)}.xs\:bui-pr-11{padding-right:var(--bui-space-11)}.xs\:bui-pr-12{padding-right:var(--bui-space-12)}.xs\:bui-pr-13{padding-right:var(--bui-space-13)}.xs\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pr{padding-right:var(--pr-sm)}.sm\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.sm\:bui-pr-1{padding-right:var(--bui-space-1)}.sm\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.sm\:bui-pr-2{padding-right:var(--bui-space-2)}.sm\:bui-pr-3{padding-right:var(--bui-space-3)}.sm\:bui-pr-4{padding-right:var(--bui-space-4)}.sm\:bui-pr-5{padding-right:var(--bui-space-5)}.sm\:bui-pr-6{padding-right:var(--bui-space-6)}.sm\:bui-pr-7{padding-right:var(--bui-space-7)}.sm\:bui-pr-8{padding-right:var(--bui-space-8)}.sm\:bui-pr-9{padding-right:var(--bui-space-9)}.sm\:bui-pr-10{padding-right:var(--bui-space-10)}.sm\:bui-pr-11{padding-right:var(--bui-space-11)}.sm\:bui-pr-12{padding-right:var(--bui-space-12)}.sm\:bui-pr-13{padding-right:var(--bui-space-13)}.sm\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pr{padding-right:var(--pr-md)}.md\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.md\:bui-pr-1{padding-right:var(--bui-space-1)}.md\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.md\:bui-pr-2{padding-right:var(--bui-space-2)}.md\:bui-pr-3{padding-right:var(--bui-space-3)}.md\:bui-pr-4{padding-right:var(--bui-space-4)}.md\:bui-pr-5{padding-right:var(--bui-space-5)}.md\:bui-pr-6{padding-right:var(--bui-space-6)}.md\:bui-pr-7{padding-right:var(--bui-space-7)}.md\:bui-pr-8{padding-right:var(--bui-space-8)}.md\:bui-pr-9{padding-right:var(--bui-space-9)}.md\:bui-pr-10{padding-right:var(--bui-space-10)}.md\:bui-pr-11{padding-right:var(--bui-space-11)}.md\:bui-pr-12{padding-right:var(--bui-space-12)}.md\:bui-pr-13{padding-right:var(--bui-space-13)}.md\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pr{padding-right:var(--pr-lg)}.lg\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.lg\:bui-pr-1{padding-right:var(--bui-space-1)}.lg\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.lg\:bui-pr-2{padding-right:var(--bui-space-2)}.lg\:bui-pr-3{padding-right:var(--bui-space-3)}.lg\:bui-pr-4{padding-right:var(--bui-space-4)}.lg\:bui-pr-5{padding-right:var(--bui-space-5)}.lg\:bui-pr-6{padding-right:var(--bui-space-6)}.lg\:bui-pr-7{padding-right:var(--bui-space-7)}.lg\:bui-pr-8{padding-right:var(--bui-space-8)}.lg\:bui-pr-9{padding-right:var(--bui-space-9)}.lg\:bui-pr-10{padding-right:var(--bui-space-10)}.lg\:bui-pr-11{padding-right:var(--bui-space-11)}.lg\:bui-pr-12{padding-right:var(--bui-space-12)}.lg\:bui-pr-13{padding-right:var(--bui-space-13)}.lg\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pr{padding-right:var(--pr-xl)}.xl\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.xl\:bui-pr-1{padding-right:var(--bui-space-1)}.xl\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.xl\:bui-pr-2{padding-right:var(--bui-space-2)}.xl\:bui-pr-3{padding-right:var(--bui-space-3)}.xl\:bui-pr-4{padding-right:var(--bui-space-4)}.xl\:bui-pr-5{padding-right:var(--bui-space-5)}.xl\:bui-pr-6{padding-right:var(--bui-space-6)}.xl\:bui-pr-7{padding-right:var(--bui-space-7)}.xl\:bui-pr-8{padding-right:var(--bui-space-8)}.xl\:bui-pr-9{padding-right:var(--bui-space-9)}.xl\:bui-pr-10{padding-right:var(--bui-space-10)}.xl\:bui-pr-11{padding-right:var(--bui-space-11)}.xl\:bui-pr-12{padding-right:var(--bui-space-12)}.xl\:bui-pr-13{padding-right:var(--bui-space-13)}.xl\:bui-pr-14{padding-right:var(--bui-space-14)}}.bui-pt{padding-top:var(--pt)}.bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.bui-pt-1{padding-top:var(--bui-space-1)}.bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.bui-pt-2{padding-top:var(--bui-space-2)}.bui-pt-3{padding-top:var(--bui-space-3)}.bui-pt-4{padding-top:var(--bui-space-4)}.bui-pt-5{padding-top:var(--bui-space-5)}.bui-pt-6{padding-top:var(--bui-space-6)}.bui-pt-7{padding-top:var(--bui-space-7)}.bui-pt-8{padding-top:var(--bui-space-8)}.bui-pt-9{padding-top:var(--bui-space-9)}.bui-pt-10{padding-top:var(--bui-space-10)}.bui-pt-11{padding-top:var(--bui-space-11)}.bui-pt-12{padding-top:var(--bui-space-12)}.bui-pt-13{padding-top:var(--bui-space-13)}.bui-pt-14{padding-top:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pt{padding-top:var(--pt-xs)}.xs\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.xs\:bui-pt-1{padding-top:var(--bui-space-1)}.xs\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.xs\:bui-pt-2{padding-top:var(--bui-space-2)}.xs\:bui-pt-3{padding-top:var(--bui-space-3)}.xs\:bui-pt-4{padding-top:var(--bui-space-4)}.xs\:bui-pt-5{padding-top:var(--bui-space-5)}.xs\:bui-pt-6{padding-top:var(--bui-space-6)}.xs\:bui-pt-7{padding-top:var(--bui-space-7)}.xs\:bui-pt-8{padding-top:var(--bui-space-8)}.xs\:bui-pt-9{padding-top:var(--bui-space-9)}.xs\:bui-pt-10{padding-top:var(--bui-space-10)}.xs\:bui-pt-11{padding-top:var(--bui-space-11)}.xs\:bui-pt-12{padding-top:var(--bui-space-12)}.xs\:bui-pt-13{padding-top:var(--bui-space-13)}.xs\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pt{padding-top:var(--pt-sm)}.sm\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.sm\:bui-pt-1{padding-top:var(--bui-space-1)}.sm\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.sm\:bui-pt-2{padding-top:var(--bui-space-2)}.sm\:bui-pt-3{padding-top:var(--bui-space-3)}.sm\:bui-pt-4{padding-top:var(--bui-space-4)}.sm\:bui-pt-5{padding-top:var(--bui-space-5)}.sm\:bui-pt-6{padding-top:var(--bui-space-6)}.sm\:bui-pt-7{padding-top:var(--bui-space-7)}.sm\:bui-pt-8{padding-top:var(--bui-space-8)}.sm\:bui-pt-9{padding-top:var(--bui-space-9)}.sm\:bui-pt-10{padding-top:var(--bui-space-10)}.sm\:bui-pt-11{padding-top:var(--bui-space-11)}.sm\:bui-pt-12{padding-top:var(--bui-space-12)}.sm\:bui-pt-13{padding-top:var(--bui-space-13)}.sm\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pt{padding-top:var(--pt-md)}.md\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.md\:bui-pt-1{padding-top:var(--bui-space-1)}.md\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.md\:bui-pt-2{padding-top:var(--bui-space-2)}.md\:bui-pt-3{padding-top:var(--bui-space-3)}.md\:bui-pt-4{padding-top:var(--bui-space-4)}.md\:bui-pt-5{padding-top:var(--bui-space-5)}.md\:bui-pt-6{padding-top:var(--bui-space-6)}.md\:bui-pt-7{padding-top:var(--bui-space-7)}.md\:bui-pt-8{padding-top:var(--bui-space-8)}.md\:bui-pt-9{padding-top:var(--bui-space-9)}.md\:bui-pt-10{padding-top:var(--bui-space-10)}.md\:bui-pt-11{padding-top:var(--bui-space-11)}.md\:bui-pt-12{padding-top:var(--bui-space-12)}.md\:bui-pt-13{padding-top:var(--bui-space-13)}.md\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pt{padding-top:var(--pt-lg)}.lg\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.lg\:bui-pt-1{padding-top:var(--bui-space-1)}.lg\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.lg\:bui-pt-2{padding-top:var(--bui-space-2)}.lg\:bui-pt-3{padding-top:var(--bui-space-3)}.lg\:bui-pt-4{padding-top:var(--bui-space-4)}.lg\:bui-pt-5{padding-top:var(--bui-space-5)}.lg\:bui-pt-6{padding-top:var(--bui-space-6)}.lg\:bui-pt-7{padding-top:var(--bui-space-7)}.lg\:bui-pt-8{padding-top:var(--bui-space-8)}.lg\:bui-pt-9{padding-top:var(--bui-space-9)}.lg\:bui-pt-10{padding-top:var(--bui-space-10)}.lg\:bui-pt-11{padding-top:var(--bui-space-11)}.lg\:bui-pt-12{padding-top:var(--bui-space-12)}.lg\:bui-pt-13{padding-top:var(--bui-space-13)}.lg\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pt{padding-top:var(--pt-xl)}.xl\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.xl\:bui-pt-1{padding-top:var(--bui-space-1)}.xl\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.xl\:bui-pt-2{padding-top:var(--bui-space-2)}.xl\:bui-pt-3{padding-top:var(--bui-space-3)}.xl\:bui-pt-4{padding-top:var(--bui-space-4)}.xl\:bui-pt-5{padding-top:var(--bui-space-5)}.xl\:bui-pt-6{padding-top:var(--bui-space-6)}.xl\:bui-pt-7{padding-top:var(--bui-space-7)}.xl\:bui-pt-8{padding-top:var(--bui-space-8)}.xl\:bui-pt-9{padding-top:var(--bui-space-9)}.xl\:bui-pt-10{padding-top:var(--bui-space-10)}.xl\:bui-pt-11{padding-top:var(--bui-space-11)}.xl\:bui-pt-12{padding-top:var(--bui-space-12)}.xl\:bui-pt-13{padding-top:var(--bui-space-13)}.xl\:bui-pt-14{padding-top:var(--bui-space-14)}}.bui-pb{padding-bottom:var(--pb)}.bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.bui-pb-1{padding-bottom:var(--bui-space-1)}.bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.bui-pb-2{padding-bottom:var(--bui-space-2)}.bui-pb-3{padding-bottom:var(--bui-space-3)}.bui-pb-4{padding-bottom:var(--bui-space-4)}.bui-pb-5{padding-bottom:var(--bui-space-5)}.bui-pb-6{padding-bottom:var(--bui-space-6)}.bui-pb-7{padding-bottom:var(--bui-space-7)}.bui-pb-8{padding-bottom:var(--bui-space-8)}.bui-pb-9{padding-bottom:var(--bui-space-9)}.bui-pb-10{padding-bottom:var(--bui-space-10)}.bui-pb-11{padding-bottom:var(--bui-space-11)}.bui-pb-12{padding-bottom:var(--bui-space-12)}.bui-pb-13{padding-bottom:var(--bui-space-13)}.bui-pb-14{padding-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pb{padding-bottom:var(--pb-xs)}.xs\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.xs\:bui-pb-1{padding-bottom:var(--bui-space-1)}.xs\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.xs\:bui-pb-2{padding-bottom:var(--bui-space-2)}.xs\:bui-pb-3{padding-bottom:var(--bui-space-3)}.xs\:bui-pb-4{padding-bottom:var(--bui-space-4)}.xs\:bui-pb-5{padding-bottom:var(--bui-space-5)}.xs\:bui-pb-6{padding-bottom:var(--bui-space-6)}.xs\:bui-pb-7{padding-bottom:var(--bui-space-7)}.xs\:bui-pb-8{padding-bottom:var(--bui-space-8)}.xs\:bui-pb-9{padding-bottom:var(--bui-space-9)}.xs\:bui-pb-10{padding-bottom:var(--bui-space-10)}.xs\:bui-pb-11{padding-bottom:var(--bui-space-11)}.xs\:bui-pb-12{padding-bottom:var(--bui-space-12)}.xs\:bui-pb-13{padding-bottom:var(--bui-space-13)}.xs\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pb{padding-bottom:var(--pb-sm)}.sm\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.sm\:bui-pb-1{padding-bottom:var(--bui-space-1)}.sm\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.sm\:bui-pb-2{padding-bottom:var(--bui-space-2)}.sm\:bui-pb-3{padding-bottom:var(--bui-space-3)}.sm\:bui-pb-4{padding-bottom:var(--bui-space-4)}.sm\:bui-pb-5{padding-bottom:var(--bui-space-5)}.sm\:bui-pb-6{padding-bottom:var(--bui-space-6)}.sm\:bui-pb-7{padding-bottom:var(--bui-space-7)}.sm\:bui-pb-8{padding-bottom:var(--bui-space-8)}.sm\:bui-pb-9{padding-bottom:var(--bui-space-9)}.sm\:bui-pb-10{padding-bottom:var(--bui-space-10)}.sm\:bui-pb-11{padding-bottom:var(--bui-space-11)}.sm\:bui-pb-12{padding-bottom:var(--bui-space-12)}.sm\:bui-pb-13{padding-bottom:var(--bui-space-13)}.sm\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pb{padding-bottom:var(--pb-md)}.md\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.md\:bui-pb-1{padding-bottom:var(--bui-space-1)}.md\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.md\:bui-pb-2{padding-bottom:var(--bui-space-2)}.md\:bui-pb-3{padding-bottom:var(--bui-space-3)}.md\:bui-pb-4{padding-bottom:var(--bui-space-4)}.md\:bui-pb-5{padding-bottom:var(--bui-space-5)}.md\:bui-pb-6{padding-bottom:var(--bui-space-6)}.md\:bui-pb-7{padding-bottom:var(--bui-space-7)}.md\:bui-pb-8{padding-bottom:var(--bui-space-8)}.md\:bui-pb-9{padding-bottom:var(--bui-space-9)}.md\:bui-pb-10{padding-bottom:var(--bui-space-10)}.md\:bui-pb-11{padding-bottom:var(--bui-space-11)}.md\:bui-pb-12{padding-bottom:var(--bui-space-12)}.md\:bui-pb-13{padding-bottom:var(--bui-space-13)}.md\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pb{padding-bottom:var(--pb-lg)}.lg\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.lg\:bui-pb-1{padding-bottom:var(--bui-space-1)}.lg\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.lg\:bui-pb-2{padding-bottom:var(--bui-space-2)}.lg\:bui-pb-3{padding-bottom:var(--bui-space-3)}.lg\:bui-pb-4{padding-bottom:var(--bui-space-4)}.lg\:bui-pb-5{padding-bottom:var(--bui-space-5)}.lg\:bui-pb-6{padding-bottom:var(--bui-space-6)}.lg\:bui-pb-7{padding-bottom:var(--bui-space-7)}.lg\:bui-pb-8{padding-bottom:var(--bui-space-8)}.lg\:bui-pb-9{padding-bottom:var(--bui-space-9)}.lg\:bui-pb-10{padding-bottom:var(--bui-space-10)}.lg\:bui-pb-11{padding-bottom:var(--bui-space-11)}.lg\:bui-pb-12{padding-bottom:var(--bui-space-12)}.lg\:bui-pb-13{padding-bottom:var(--bui-space-13)}.lg\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pb{padding-bottom:var(--pb-xl)}.xl\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.xl\:bui-pb-1{padding-bottom:var(--bui-space-1)}.xl\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.xl\:bui-pb-2{padding-bottom:var(--bui-space-2)}.xl\:bui-pb-3{padding-bottom:var(--bui-space-3)}.xl\:bui-pb-4{padding-bottom:var(--bui-space-4)}.xl\:bui-pb-5{padding-bottom:var(--bui-space-5)}.xl\:bui-pb-6{padding-bottom:var(--bui-space-6)}.xl\:bui-pb-7{padding-bottom:var(--bui-space-7)}.xl\:bui-pb-8{padding-bottom:var(--bui-space-8)}.xl\:bui-pb-9{padding-bottom:var(--bui-space-9)}.xl\:bui-pb-10{padding-bottom:var(--bui-space-10)}.xl\:bui-pb-11{padding-bottom:var(--bui-space-11)}.xl\:bui-pb-12{padding-bottom:var(--bui-space-12)}.xl\:bui-pb-13{padding-bottom:var(--bui-space-13)}.xl\:bui-pb-14{padding-bottom:var(--bui-space-14)}}.bui-py{padding-top:var(--py);padding-bottom:var(--py)}.bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-py{padding-top:var(--py-xs);padding-bottom:var(--py-xs)}.xs\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.xs\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.xs\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.xs\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.xs\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.xs\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.xs\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.xs\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.xs\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.xs\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.xs\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.xs\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.xs\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.xs\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.xs\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.xs\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-py{padding-top:var(--py-sm);padding-bottom:var(--py-sm)}.sm\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.sm\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.sm\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.sm\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.sm\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.sm\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.sm\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.sm\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.sm\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.sm\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.sm\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.sm\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.sm\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.sm\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.sm\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.sm\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-py{padding-top:var(--py-md);padding-bottom:var(--py-md)}.md\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.md\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.md\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.md\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.md\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.md\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.md\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.md\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.md\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.md\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.md\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.md\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.md\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.md\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.md\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.md\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-py{padding-top:var(--py-lg);padding-bottom:var(--py-lg)}.lg\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.lg\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.lg\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.lg\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.lg\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.lg\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.lg\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.lg\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.lg\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.lg\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.lg\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.lg\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.lg\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.lg\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.lg\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.lg\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-py{padding-top:var(--py-xl);padding-bottom:var(--py-xl)}.xl\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.xl\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.xl\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.xl\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.xl\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.xl\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.xl\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.xl\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.xl\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.xl\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.xl\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.xl\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.xl\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.xl\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.xl\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.xl\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}.bui-px{padding-left:var(--px);padding-right:var(--px)}.bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-px{padding-left:var(--px-xs);padding-right:var(--px-xs)}.xs\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.xs\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.xs\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.xs\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.xs\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.xs\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.xs\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.xs\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.xs\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.xs\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.xs\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.xs\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.xs\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.xs\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.xs\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.xs\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-px{padding-left:var(--px-sm);padding-right:var(--px-sm)}.sm\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.sm\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.sm\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.sm\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.sm\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.sm\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.sm\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.sm\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.sm\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.sm\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.sm\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.sm\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.sm\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.sm\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.sm\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.sm\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-px{padding-left:var(--px-md);padding-right:var(--px-md)}.md\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.md\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.md\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.md\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.md\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.md\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.md\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.md\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.md\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.md\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.md\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.md\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.md\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.md\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.md\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.md\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-px{padding-left:var(--px-lg);padding-right:var(--px-lg)}.lg\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.lg\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.lg\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.lg\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.lg\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.lg\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.lg\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.lg\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.lg\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.lg\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.lg\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.lg\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.lg\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.lg\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.lg\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.lg\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-px{padding-left:var(--px-xl);padding-right:var(--px-xl)}.xl\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.xl\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.xl\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.xl\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.xl\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.xl\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.xl\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.xl\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.xl\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.xl\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.xl\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.xl\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.xl\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.xl\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.xl\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.xl\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}.bui-m{margin:var(--m)}.bui-m-0\.5{margin:var(--bui-space-0_5)}.bui-m-1{margin:var(--bui-space-1)}.bui-m-1\.5{margin:var(--bui-space-1_5)}.bui-m-2{margin:var(--bui-space-2)}.bui-m-3{margin:var(--bui-space-3)}.bui-m-4{margin:var(--bui-space-4)}.bui-m-5{margin:var(--bui-space-5)}.bui-m-6{margin:var(--bui-space-6)}.bui-m-7{margin:var(--bui-space-7)}.bui-m-8{margin:var(--bui-space-8)}.bui-m-9{margin:var(--bui-space-9)}.bui-m-10{margin:var(--bui-space-10)}.bui-m-11{margin:var(--bui-space-11)}.bui-m-12{margin:var(--bui-space-12)}.bui-m-13{margin:var(--bui-space-13)}.bui-m-14{margin:var(--bui-space-14)}@media (width>=640px){.xs\:bui-m{margin:var(--p-xs)}.xs\:bui-m-0\.5{margin:var(--bui-space-0_5)}.xs\:bui-m-1{margin:var(--bui-space-1)}.xs\:bui-m-1\.5{margin:var(--bui-space-1_5)}.xs\:bui-m-2{margin:var(--bui-space-2)}.xs\:bui-m-3{margin:var(--bui-space-3)}.xs\:bui-m-4{margin:var(--bui-space-4)}.xs\:bui-m-5{margin:var(--bui-space-5)}.xs\:bui-m-6{margin:var(--bui-space-6)}.xs\:bui-m-7{margin:var(--bui-space-7)}.xs\:bui-m-8{margin:var(--bui-space-8)}.xs\:bui-m-9{margin:var(--bui-space-9)}.xs\:bui-m-10{margin:var(--bui-space-10)}.xs\:bui-m-11{margin:var(--bui-space-11)}.xs\:bui-m-12{margin:var(--bui-space-12)}.xs\:bui-m-13{margin:var(--bui-space-13)}.xs\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-m{margin:var(--p-sm)}.sm\:bui-m-0\.5{margin:var(--bui-space-0_5)}.sm\:bui-m-1{margin:var(--bui-space-1)}.sm\:bui-m-1\.5{margin:var(--bui-space-1_5)}.sm\:bui-m-2{margin:var(--bui-space-2)}.sm\:bui-m-3{margin:var(--bui-space-3)}.sm\:bui-m-4{margin:var(--bui-space-4)}.sm\:bui-m-5{margin:var(--bui-space-5)}.sm\:bui-m-6{margin:var(--bui-space-6)}.sm\:bui-m-7{margin:var(--bui-space-7)}.sm\:bui-m-8{margin:var(--bui-space-8)}.sm\:bui-m-9{margin:var(--bui-space-9)}.sm\:bui-m-10{margin:var(--bui-space-10)}.sm\:bui-m-11{margin:var(--bui-space-11)}.sm\:bui-m-12{margin:var(--bui-space-12)}.sm\:bui-m-13{margin:var(--bui-space-13)}.sm\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-m{margin:var(--p-md)}.md\:bui-m-0\.5{margin:var(--bui-space-0_5)}.md\:bui-m-1{margin:var(--bui-space-1)}.md\:bui-m-1\.5{margin:var(--bui-space-1_5)}.md\:bui-m-2{margin:var(--bui-space-2)}.md\:bui-m-3{margin:var(--bui-space-3)}.md\:bui-m-4{margin:var(--bui-space-4)}.md\:bui-m-5{margin:var(--bui-space-5)}.md\:bui-m-6{margin:var(--bui-space-6)}.md\:bui-m-7{margin:var(--bui-space-7)}.md\:bui-m-8{margin:var(--bui-space-8)}.md\:bui-m-9{margin:var(--bui-space-9)}.md\:bui-m-10{margin:var(--bui-space-10)}.md\:bui-m-11{margin:var(--bui-space-11)}.md\:bui-m-12{margin:var(--bui-space-12)}.md\:bui-m-13{margin:var(--bui-space-13)}.md\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-m{margin:var(--p-lg)}.lg\:bui-m-0\.5{margin:var(--bui-space-0_5)}.lg\:bui-m-1{margin:var(--bui-space-1)}.lg\:bui-m-1\.5{margin:var(--bui-space-1_5)}.lg\:bui-m-2{margin:var(--bui-space-2)}.lg\:bui-m-3{margin:var(--bui-space-3)}.lg\:bui-m-4{margin:var(--bui-space-4)}.lg\:bui-m-5{margin:var(--bui-space-5)}.lg\:bui-m-6{margin:var(--bui-space-6)}.lg\:bui-m-7{margin:var(--bui-space-7)}.lg\:bui-m-8{margin:var(--bui-space-8)}.lg\:bui-m-9{margin:var(--bui-space-9)}.lg\:bui-m-10{margin:var(--bui-space-10)}.lg\:bui-m-11{margin:var(--bui-space-11)}.lg\:bui-m-12{margin:var(--bui-space-12)}.lg\:bui-m-13{margin:var(--bui-space-13)}.lg\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-m{margin:var(--p-xl)}.xl\:bui-m-0\.5{margin:var(--bui-space-0_5)}.xl\:bui-m-1{margin:var(--bui-space-1)}.xl\:bui-m-1\.5{margin:var(--bui-space-1_5)}.xl\:bui-m-2{margin:var(--bui-space-2)}.xl\:bui-m-3{margin:var(--bui-space-3)}.xl\:bui-m-4{margin:var(--bui-space-4)}.xl\:bui-m-5{margin:var(--bui-space-5)}.xl\:bui-m-6{margin:var(--bui-space-6)}.xl\:bui-m-7{margin:var(--bui-space-7)}.xl\:bui-m-8{margin:var(--bui-space-8)}.xl\:bui-m-9{margin:var(--bui-space-9)}.xl\:bui-m-10{margin:var(--bui-space-10)}.xl\:bui-m-11{margin:var(--bui-space-11)}.xl\:bui-m-12{margin:var(--bui-space-12)}.xl\:bui-m-13{margin:var(--bui-space-13)}.xl\:bui-m-14{margin:var(--bui-space-14)}}.bui-ml{margin-left:var(--ml)}.bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.bui-ml-1{margin-left:var(--bui-space-1)}.bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.bui-ml-2{margin-left:var(--bui-space-2)}.bui-ml-3{margin-left:var(--bui-space-3)}.bui-ml-4{margin-left:var(--bui-space-4)}.bui-ml-5{margin-left:var(--bui-space-5)}.bui-ml-6{margin-left:var(--bui-space-6)}.bui-ml-7{margin-left:var(--bui-space-7)}.bui-ml-8{margin-left:var(--bui-space-8)}.bui-ml-9{margin-left:var(--bui-space-9)}.bui-ml-10{margin-left:var(--bui-space-10)}.bui-ml-11{margin-left:var(--bui-space-11)}.bui-ml-12{margin-left:var(--bui-space-12)}.bui-ml-13{margin-left:var(--bui-space-13)}.bui-ml-14{margin-left:var(--bui-space-14)}@media (width>=640px){.xs\:bui-ml{margin-left:var(--ml-xs)}.xs\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.xs\:bui-ml-1{margin-left:var(--bui-space-1)}.xs\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.xs\:bui-ml-2{margin-left:var(--bui-space-2)}.xs\:bui-ml-3{margin-left:var(--bui-space-3)}.xs\:bui-ml-4{margin-left:var(--bui-space-4)}.xs\:bui-ml-5{margin-left:var(--bui-space-5)}.xs\:bui-ml-6{margin-left:var(--bui-space-6)}.xs\:bui-ml-7{margin-left:var(--bui-space-7)}.xs\:bui-ml-8{margin-left:var(--bui-space-8)}.xs\:bui-ml-9{margin-left:var(--bui-space-9)}.xs\:bui-ml-10{margin-left:var(--bui-space-10)}.xs\:bui-ml-11{margin-left:var(--bui-space-11)}.xs\:bui-ml-12{margin-left:var(--bui-space-12)}.xs\:bui-ml-13{margin-left:var(--bui-space-13)}.xs\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-ml{margin-left:var(--ml-sm)}.sm\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.sm\:bui-ml-1{margin-left:var(--bui-space-1)}.sm\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.sm\:bui-ml-2{margin-left:var(--bui-space-2)}.sm\:bui-ml-3{margin-left:var(--bui-space-3)}.sm\:bui-ml-4{margin-left:var(--bui-space-4)}.sm\:bui-ml-5{margin-left:var(--bui-space-5)}.sm\:bui-ml-6{margin-left:var(--bui-space-6)}.sm\:bui-ml-7{margin-left:var(--bui-space-7)}.sm\:bui-ml-8{margin-left:var(--bui-space-8)}.sm\:bui-ml-9{margin-left:var(--bui-space-9)}.sm\:bui-ml-10{margin-left:var(--bui-space-10)}.sm\:bui-ml-11{margin-left:var(--bui-space-11)}.sm\:bui-ml-12{margin-left:var(--bui-space-12)}.sm\:bui-ml-13{margin-left:var(--bui-space-13)}.sm\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-ml{margin-left:var(--ml-md)}.md\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.md\:bui-ml-1{margin-left:var(--bui-space-1)}.md\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.md\:bui-ml-2{margin-left:var(--bui-space-2)}.md\:bui-ml-3{margin-left:var(--bui-space-3)}.md\:bui-ml-4{margin-left:var(--bui-space-4)}.md\:bui-ml-5{margin-left:var(--bui-space-5)}.md\:bui-ml-6{margin-left:var(--bui-space-6)}.md\:bui-ml-7{margin-left:var(--bui-space-7)}.md\:bui-ml-8{margin-left:var(--bui-space-8)}.md\:bui-ml-9{margin-left:var(--bui-space-9)}.md\:bui-ml-10{margin-left:var(--bui-space-10)}.md\:bui-ml-11{margin-left:var(--bui-space-11)}.md\:bui-ml-12{margin-left:var(--bui-space-12)}.md\:bui-ml-13{margin-left:var(--bui-space-13)}.md\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-ml{margin-left:var(--ml-lg)}.lg\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.lg\:bui-ml-1{margin-left:var(--bui-space-1)}.lg\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.lg\:bui-ml-2{margin-left:var(--bui-space-2)}.lg\:bui-ml-3{margin-left:var(--bui-space-3)}.lg\:bui-ml-4{margin-left:var(--bui-space-4)}.lg\:bui-ml-5{margin-left:var(--bui-space-5)}.lg\:bui-ml-6{margin-left:var(--bui-space-6)}.lg\:bui-ml-7{margin-left:var(--bui-space-7)}.lg\:bui-ml-8{margin-left:var(--bui-space-8)}.lg\:bui-ml-9{margin-left:var(--bui-space-9)}.lg\:bui-ml-10{margin-left:var(--bui-space-10)}.lg\:bui-ml-11{margin-left:var(--bui-space-11)}.lg\:bui-ml-12{margin-left:var(--bui-space-12)}.lg\:bui-ml-13{margin-left:var(--bui-space-13)}.lg\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-ml{margin-left:var(--ml-xl)}.xl\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.xl\:bui-ml-1{margin-left:var(--bui-space-1)}.xl\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.xl\:bui-ml-2{margin-left:var(--bui-space-2)}.xl\:bui-ml-3{margin-left:var(--bui-space-3)}.xl\:bui-ml-4{margin-left:var(--bui-space-4)}.xl\:bui-ml-5{margin-left:var(--bui-space-5)}.xl\:bui-ml-6{margin-left:var(--bui-space-6)}.xl\:bui-ml-7{margin-left:var(--bui-space-7)}.xl\:bui-ml-8{margin-left:var(--bui-space-8)}.xl\:bui-ml-9{margin-left:var(--bui-space-9)}.xl\:bui-ml-10{margin-left:var(--bui-space-10)}.xl\:bui-ml-11{margin-left:var(--bui-space-11)}.xl\:bui-ml-12{margin-left:var(--bui-space-12)}.xl\:bui-ml-13{margin-left:var(--bui-space-13)}.xl\:bui-ml-14{margin-left:var(--bui-space-14)}}.bui-mr{margin-right:var(--mr)}.bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.bui-mr-1{margin-right:var(--bui-space-1)}.bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.bui-mr-2{margin-right:var(--bui-space-2)}.bui-mr-3{margin-right:var(--bui-space-3)}.bui-mr-4{margin-right:var(--bui-space-4)}.bui-mr-5{margin-right:var(--bui-space-5)}.bui-mr-6{margin-right:var(--bui-space-6)}.bui-mr-7{margin-right:var(--bui-space-7)}.bui-mr-8{margin-right:var(--bui-space-8)}.bui-mr-9{margin-right:var(--bui-space-9)}.bui-mr-10{margin-right:var(--bui-space-10)}.bui-mr-11{margin-right:var(--bui-space-11)}.bui-mr-12{margin-right:var(--bui-space-12)}.bui-mr-13{margin-right:var(--bui-space-13)}.bui-mr-14{margin-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mr{margin-right:var(--mr-xs)}.xs\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.xs\:bui-mr-1{margin-right:var(--bui-space-1)}.xs\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.xs\:bui-mr-2{margin-right:var(--bui-space-2)}.xs\:bui-mr-3{margin-right:var(--bui-space-3)}.xs\:bui-mr-4{margin-right:var(--bui-space-4)}.xs\:bui-mr-5{margin-right:var(--bui-space-5)}.xs\:bui-mr-6{margin-right:var(--bui-space-6)}.xs\:bui-mr-7{margin-right:var(--bui-space-7)}.xs\:bui-mr-8{margin-right:var(--bui-space-8)}.xs\:bui-mr-9{margin-right:var(--bui-space-9)}.xs\:bui-mr-10{margin-right:var(--bui-space-10)}.xs\:bui-mr-11{margin-right:var(--bui-space-11)}.xs\:bui-mr-12{margin-right:var(--bui-space-12)}.xs\:bui-mr-13{margin-right:var(--bui-space-13)}.xs\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mr{margin-right:var(--mr-sm)}.sm\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.sm\:bui-mr-1{margin-right:var(--bui-space-1)}.sm\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.sm\:bui-mr-2{margin-right:var(--bui-space-2)}.sm\:bui-mr-3{margin-right:var(--bui-space-3)}.sm\:bui-mr-4{margin-right:var(--bui-space-4)}.sm\:bui-mr-5{margin-right:var(--bui-space-5)}.sm\:bui-mr-6{margin-right:var(--bui-space-6)}.sm\:bui-mr-7{margin-right:var(--bui-space-7)}.sm\:bui-mr-8{margin-right:var(--bui-space-8)}.sm\:bui-mr-9{margin-right:var(--bui-space-9)}.sm\:bui-mr-10{margin-right:var(--bui-space-10)}.sm\:bui-mr-11{margin-right:var(--bui-space-11)}.sm\:bui-mr-12{margin-right:var(--bui-space-12)}.sm\:bui-mr-13{margin-right:var(--bui-space-13)}.sm\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mr{margin-right:var(--mr-md)}.md\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.md\:bui-mr-1{margin-right:var(--bui-space-1)}.md\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.md\:bui-mr-2{margin-right:var(--bui-space-2)}.md\:bui-mr-3{margin-right:var(--bui-space-3)}.md\:bui-mr-4{margin-right:var(--bui-space-4)}.md\:bui-mr-5{margin-right:var(--bui-space-5)}.md\:bui-mr-6{margin-right:var(--bui-space-6)}.md\:bui-mr-7{margin-right:var(--bui-space-7)}.md\:bui-mr-8{margin-right:var(--bui-space-8)}.md\:bui-mr-9{margin-right:var(--bui-space-9)}.md\:bui-mr-10{margin-right:var(--bui-space-10)}.md\:bui-mr-11{margin-right:var(--bui-space-11)}.md\:bui-mr-12{margin-right:var(--bui-space-12)}.md\:bui-mr-13{margin-right:var(--bui-space-13)}.md\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mr{margin-right:var(--mr-lg)}.lg\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.lg\:bui-mr-1{margin-right:var(--bui-space-1)}.lg\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.lg\:bui-mr-2{margin-right:var(--bui-space-2)}.lg\:bui-mr-3{margin-right:var(--bui-space-3)}.lg\:bui-mr-4{margin-right:var(--bui-space-4)}.lg\:bui-mr-5{margin-right:var(--bui-space-5)}.lg\:bui-mr-6{margin-right:var(--bui-space-6)}.lg\:bui-mr-7{margin-right:var(--bui-space-7)}.lg\:bui-mr-8{margin-right:var(--bui-space-8)}.lg\:bui-mr-9{margin-right:var(--bui-space-9)}.lg\:bui-mr-10{margin-right:var(--bui-space-10)}.lg\:bui-mr-11{margin-right:var(--bui-space-11)}.lg\:bui-mr-12{margin-right:var(--bui-space-12)}.lg\:bui-mr-13{margin-right:var(--bui-space-13)}.lg\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mr{margin-right:var(--mr-xl)}.xl\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.xl\:bui-mr-1{margin-right:var(--bui-space-1)}.xl\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.xl\:bui-mr-2{margin-right:var(--bui-space-2)}.xl\:bui-mr-3{margin-right:var(--bui-space-3)}.xl\:bui-mr-4{margin-right:var(--bui-space-4)}.xl\:bui-mr-5{margin-right:var(--bui-space-5)}.xl\:bui-mr-6{margin-right:var(--bui-space-6)}.xl\:bui-mr-7{margin-right:var(--bui-space-7)}.xl\:bui-mr-8{margin-right:var(--bui-space-8)}.xl\:bui-mr-9{margin-right:var(--bui-space-9)}.xl\:bui-mr-10{margin-right:var(--bui-space-10)}.xl\:bui-mr-11{margin-right:var(--bui-space-11)}.xl\:bui-mr-12{margin-right:var(--bui-space-12)}.xl\:bui-mr-13{margin-right:var(--bui-space-13)}.xl\:bui-mr-14{margin-right:var(--bui-space-14)}}.bui-mt{margin-top:var(--mt)}.bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.bui-mt-1{margin-top:var(--bui-space-1)}.bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.bui-mt-2{margin-top:var(--bui-space-2)}.bui-mt-3{margin-top:var(--bui-space-3)}.bui-mt-4{margin-top:var(--bui-space-4)}.bui-mt-5{margin-top:var(--bui-space-5)}.bui-mt-6{margin-top:var(--bui-space-6)}.bui-mt-7{margin-top:var(--bui-space-7)}.bui-mt-8{margin-top:var(--bui-space-8)}.bui-mt-9{margin-top:var(--bui-space-9)}.bui-mt-10{margin-top:var(--bui-space-10)}.bui-mt-11{margin-top:var(--bui-space-11)}.bui-mt-12{margin-top:var(--bui-space-12)}.bui-mt-13{margin-top:var(--bui-space-13)}.bui-mt-14{margin-top:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mt{margin-top:var(--mt-xs)}.xs\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.xs\:bui-mt-1{margin-top:var(--bui-space-1)}.xs\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.xs\:bui-mt-2{margin-top:var(--bui-space-2)}.xs\:bui-mt-3{margin-top:var(--bui-space-3)}.xs\:bui-mt-4{margin-top:var(--bui-space-4)}.xs\:bui-mt-5{margin-top:var(--bui-space-5)}.xs\:bui-mt-6{margin-top:var(--bui-space-6)}.xs\:bui-mt-7{margin-top:var(--bui-space-7)}.xs\:bui-mt-8{margin-top:var(--bui-space-8)}.xs\:bui-mt-9{margin-top:var(--bui-space-9)}.xs\:bui-mt-10{margin-top:var(--bui-space-10)}.xs\:bui-mt-11{margin-top:var(--bui-space-11)}.xs\:bui-mt-12{margin-top:var(--bui-space-12)}.xs\:bui-mt-13{margin-top:var(--bui-space-13)}.xs\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mt{margin-top:var(--mt-sm)}.sm\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.sm\:bui-mt-1{margin-top:var(--bui-space-1)}.sm\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.sm\:bui-mt-2{margin-top:var(--bui-space-2)}.sm\:bui-mt-3{margin-top:var(--bui-space-3)}.sm\:bui-mt-4{margin-top:var(--bui-space-4)}.sm\:bui-mt-5{margin-top:var(--bui-space-5)}.sm\:bui-mt-6{margin-top:var(--bui-space-6)}.sm\:bui-mt-7{margin-top:var(--bui-space-7)}.sm\:bui-mt-8{margin-top:var(--bui-space-8)}.sm\:bui-mt-9{margin-top:var(--bui-space-9)}.sm\:bui-mt-10{margin-top:var(--bui-space-10)}.sm\:bui-mt-11{margin-top:var(--bui-space-11)}.sm\:bui-mt-12{margin-top:var(--bui-space-12)}.sm\:bui-mt-13{margin-top:var(--bui-space-13)}.sm\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mt{margin-top:var(--mt-md)}.md\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.md\:bui-mt-1{margin-top:var(--bui-space-1)}.md\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.md\:bui-mt-2{margin-top:var(--bui-space-2)}.md\:bui-mt-3{margin-top:var(--bui-space-3)}.md\:bui-mt-4{margin-top:var(--bui-space-4)}.md\:bui-mt-5{margin-top:var(--bui-space-5)}.md\:bui-mt-6{margin-top:var(--bui-space-6)}.md\:bui-mt-7{margin-top:var(--bui-space-7)}.md\:bui-mt-8{margin-top:var(--bui-space-8)}.md\:bui-mt-9{margin-top:var(--bui-space-9)}.md\:bui-mt-10{margin-top:var(--bui-space-10)}.md\:bui-mt-11{margin-top:var(--bui-space-11)}.md\:bui-mt-12{margin-top:var(--bui-space-12)}.md\:bui-mt-13{margin-top:var(--bui-space-13)}.md\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mt{margin-top:var(--mt-lg)}.lg\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.lg\:bui-mt-1{margin-top:var(--bui-space-1)}.lg\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.lg\:bui-mt-2{margin-top:var(--bui-space-2)}.lg\:bui-mt-3{margin-top:var(--bui-space-3)}.lg\:bui-mt-4{margin-top:var(--bui-space-4)}.lg\:bui-mt-5{margin-top:var(--bui-space-5)}.lg\:bui-mt-6{margin-top:var(--bui-space-6)}.lg\:bui-mt-7{margin-top:var(--bui-space-7)}.lg\:bui-mt-8{margin-top:var(--bui-space-8)}.lg\:bui-mt-9{margin-top:var(--bui-space-9)}.lg\:bui-mt-10{margin-top:var(--bui-space-10)}.lg\:bui-mt-11{margin-top:var(--bui-space-11)}.lg\:bui-mt-12{margin-top:var(--bui-space-12)}.lg\:bui-mt-13{margin-top:var(--bui-space-13)}.lg\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mt{margin-top:var(--mt-xl)}.xl\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.xl\:bui-mt-1{margin-top:var(--bui-space-1)}.xl\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.xl\:bui-mt-2{margin-top:var(--bui-space-2)}.xl\:bui-mt-3{margin-top:var(--bui-space-3)}.xl\:bui-mt-4{margin-top:var(--bui-space-4)}.xl\:bui-mt-5{margin-top:var(--bui-space-5)}.xl\:bui-mt-6{margin-top:var(--bui-space-6)}.xl\:bui-mt-7{margin-top:var(--bui-space-7)}.xl\:bui-mt-8{margin-top:var(--bui-space-8)}.xl\:bui-mt-9{margin-top:var(--bui-space-9)}.xl\:bui-mt-10{margin-top:var(--bui-space-10)}.xl\:bui-mt-11{margin-top:var(--bui-space-11)}.xl\:bui-mt-12{margin-top:var(--bui-space-12)}.xl\:bui-mt-13{margin-top:var(--bui-space-13)}.xl\:bui-mt-14{margin-top:var(--bui-space-14)}}.bui-mb{margin-bottom:var(--mb)}.bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.bui-mb-1{margin-bottom:var(--bui-space-1)}.bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.bui-mb-2{margin-bottom:var(--bui-space-2)}.bui-mb-3{margin-bottom:var(--bui-space-3)}.bui-mb-4{margin-bottom:var(--bui-space-4)}.bui-mb-5{margin-bottom:var(--bui-space-5)}.bui-mb-6{margin-bottom:var(--bui-space-6)}.bui-mb-7{margin-bottom:var(--bui-space-7)}.bui-mb-8{margin-bottom:var(--bui-space-8)}.bui-mb-9{margin-bottom:var(--bui-space-9)}.bui-mb-10{margin-bottom:var(--bui-space-10)}.bui-mb-11{margin-bottom:var(--bui-space-11)}.bui-mb-12{margin-bottom:var(--bui-space-12)}.bui-mb-13{margin-bottom:var(--bui-space-13)}.bui-mb-14{margin-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mb{margin-bottom:var(--mb-xs)}.xs\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.xs\:bui-mb-1{margin-bottom:var(--bui-space-1)}.xs\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.xs\:bui-mb-2{margin-bottom:var(--bui-space-2)}.xs\:bui-mb-3{margin-bottom:var(--bui-space-3)}.xs\:bui-mb-4{margin-bottom:var(--bui-space-4)}.xs\:bui-mb-5{margin-bottom:var(--bui-space-5)}.xs\:bui-mb-6{margin-bottom:var(--bui-space-6)}.xs\:bui-mb-7{margin-bottom:var(--bui-space-7)}.xs\:bui-mb-8{margin-bottom:var(--bui-space-8)}.xs\:bui-mb-9{margin-bottom:var(--bui-space-9)}.xs\:bui-mb-10{margin-bottom:var(--bui-space-10)}.xs\:bui-mb-11{margin-bottom:var(--bui-space-11)}.xs\:bui-mb-12{margin-bottom:var(--bui-space-12)}.xs\:bui-mb-13{margin-bottom:var(--bui-space-13)}.xs\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mb{margin-bottom:var(--mb-sm)}.sm\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.sm\:bui-mb-1{margin-bottom:var(--bui-space-1)}.sm\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.sm\:bui-mb-2{margin-bottom:var(--bui-space-2)}.sm\:bui-mb-3{margin-bottom:var(--bui-space-3)}.sm\:bui-mb-4{margin-bottom:var(--bui-space-4)}.sm\:bui-mb-5{margin-bottom:var(--bui-space-5)}.sm\:bui-mb-6{margin-bottom:var(--bui-space-6)}.sm\:bui-mb-7{margin-bottom:var(--bui-space-7)}.sm\:bui-mb-8{margin-bottom:var(--bui-space-8)}.sm\:bui-mb-9{margin-bottom:var(--bui-space-9)}.sm\:bui-mb-10{margin-bottom:var(--bui-space-10)}.sm\:bui-mb-11{margin-bottom:var(--bui-space-11)}.sm\:bui-mb-12{margin-bottom:var(--bui-space-12)}.sm\:bui-mb-13{margin-bottom:var(--bui-space-13)}.sm\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mb{margin-bottom:var(--mb-md)}.md\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.md\:bui-mb-1{margin-bottom:var(--bui-space-1)}.md\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.md\:bui-mb-2{margin-bottom:var(--bui-space-2)}.md\:bui-mb-3{margin-bottom:var(--bui-space-3)}.md\:bui-mb-4{margin-bottom:var(--bui-space-4)}.md\:bui-mb-5{margin-bottom:var(--bui-space-5)}.md\:bui-mb-6{margin-bottom:var(--bui-space-6)}.md\:bui-mb-7{margin-bottom:var(--bui-space-7)}.md\:bui-mb-8{margin-bottom:var(--bui-space-8)}.md\:bui-mb-9{margin-bottom:var(--bui-space-9)}.md\:bui-mb-10{margin-bottom:var(--bui-space-10)}.md\:bui-mb-11{margin-bottom:var(--bui-space-11)}.md\:bui-mb-12{margin-bottom:var(--bui-space-12)}.md\:bui-mb-13{margin-bottom:var(--bui-space-13)}.md\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mb{margin-bottom:var(--mb-lg)}.lg\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.lg\:bui-mb-1{margin-bottom:var(--bui-space-1)}.lg\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.lg\:bui-mb-2{margin-bottom:var(--bui-space-2)}.lg\:bui-mb-3{margin-bottom:var(--bui-space-3)}.lg\:bui-mb-4{margin-bottom:var(--bui-space-4)}.lg\:bui-mb-5{margin-bottom:var(--bui-space-5)}.lg\:bui-mb-6{margin-bottom:var(--bui-space-6)}.lg\:bui-mb-7{margin-bottom:var(--bui-space-7)}.lg\:bui-mb-8{margin-bottom:var(--bui-space-8)}.lg\:bui-mb-9{margin-bottom:var(--bui-space-9)}.lg\:bui-mb-10{margin-bottom:var(--bui-space-10)}.lg\:bui-mb-11{margin-bottom:var(--bui-space-11)}.lg\:bui-mb-12{margin-bottom:var(--bui-space-12)}.lg\:bui-mb-13{margin-bottom:var(--bui-space-13)}.lg\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mb{margin-bottom:var(--mb-xl)}.xl\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.xl\:bui-mb-1{margin-bottom:var(--bui-space-1)}.xl\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.xl\:bui-mb-2{margin-bottom:var(--bui-space-2)}.xl\:bui-mb-3{margin-bottom:var(--bui-space-3)}.xl\:bui-mb-4{margin-bottom:var(--bui-space-4)}.xl\:bui-mb-5{margin-bottom:var(--bui-space-5)}.xl\:bui-mb-6{margin-bottom:var(--bui-space-6)}.xl\:bui-mb-7{margin-bottom:var(--bui-space-7)}.xl\:bui-mb-8{margin-bottom:var(--bui-space-8)}.xl\:bui-mb-9{margin-bottom:var(--bui-space-9)}.xl\:bui-mb-10{margin-bottom:var(--bui-space-10)}.xl\:bui-mb-11{margin-bottom:var(--bui-space-11)}.xl\:bui-mb-12{margin-bottom:var(--bui-space-12)}.xl\:bui-mb-13{margin-bottom:var(--bui-space-13)}.xl\:bui-mb-14{margin-bottom:var(--bui-space-14)}}.bui-my{margin-top:var(--my);margin-bottom:var(--my)}.bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-my{margin-top:var(--my-xs);margin-bottom:var(--my-xs)}.xs\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.xs\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.xs\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.xs\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.xs\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.xs\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.xs\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.xs\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.xs\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.xs\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.xs\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.xs\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.xs\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.xs\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.xs\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.xs\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-my{margin-top:var(--my-sm);margin-bottom:var(--my-sm)}.sm\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.sm\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.sm\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.sm\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.sm\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.sm\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.sm\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.sm\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.sm\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.sm\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.sm\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.sm\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.sm\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.sm\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.sm\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.sm\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-my{margin-top:var(--my-md);margin-bottom:var(--my-md)}.md\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.md\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.md\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.md\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.md\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.md\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.md\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.md\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.md\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.md\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.md\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.md\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.md\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.md\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.md\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.md\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-my{margin-top:var(--my-lg);margin-bottom:var(--my-lg)}.lg\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.lg\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.lg\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.lg\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.lg\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.lg\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.lg\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.lg\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.lg\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.lg\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.lg\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.lg\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.lg\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.lg\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.lg\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.lg\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-my{margin-top:var(--my-xl);margin-bottom:var(--my-xl)}.xl\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.xl\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.xl\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.xl\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.xl\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.xl\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.xl\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.xl\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.xl\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.xl\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.xl\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.xl\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.xl\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.xl\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.xl\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.xl\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}.bui-mx{margin-left:var(--mx);margin-right:var(--mx)}.bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mx{margin-left:var(--mx-xs);margin-right:var(--mx-xs)}.xs\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.xs\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.xs\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.xs\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.xs\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.xs\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.xs\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.xs\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.xs\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.xs\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.xs\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.xs\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.xs\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.xs\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.xs\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.xs\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mx{margin-left:var(--mx-sm);margin-right:var(--mx-sm)}.sm\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.sm\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.sm\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.sm\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.sm\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.sm\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.sm\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.sm\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.sm\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.sm\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.sm\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.sm\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.sm\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.sm\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.sm\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.sm\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mx{margin-left:var(--mx-md);margin-right:var(--mx-md)}.md\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.md\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.md\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.md\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.md\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.md\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.md\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.md\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.md\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.md\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.md\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.md\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.md\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.md\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.md\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.md\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mx{margin-left:var(--mx-lg);margin-right:var(--mx-lg)}.lg\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.lg\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.lg\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.lg\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.lg\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.lg\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.lg\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.lg\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.lg\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.lg\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.lg\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.lg\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.lg\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.lg\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.lg\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.lg\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mx{margin-left:var(--mx-xl);margin-right:var(--mx-xl)}.xl\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.xl\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.xl\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.xl\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.xl\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.xl\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.xl\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.xl\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.xl\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.xl\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.xl\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.xl\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.xl\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.xl\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.xl\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.xl\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}.bui-display-none{display:none}.bui-display-inline{display:inline}.bui-display-inline-block{display:inline-block}.bui-display-block{display:block}@media (width>=640px){.xs\:bui-display-none{display:none}.xs\:bui-display-inline{display:inline}.xs\:bui-display-inline-block{display:inline-block}.xs\:bui-display-block{display:block}}@media (width>=768px){.sm\:bui-display-none{display:none}.sm\:bui-display-inline{display:inline}.sm\:bui-display-inline-block{display:inline-block}.sm\:bui-display-block{display:block}}@media (width>=1024px){.md\:bui-display-none{display:none}.md\:bui-display-inline{display:inline}.md\:bui-display-inline-block{display:inline-block}.md\:bui-display-block{display:block}}@media (width>=1280px){.lg\:bui-display-none{display:none}.lg\:bui-display-inline{display:inline}.lg\:bui-display-inline-block{display:inline-block}.lg\:bui-display-block{display:block}}@media (width>=1536px){.xl\:bui-display-none{display:none}.xl\:bui-display-inline{display:inline}.xl\:bui-display-inline-block{display:inline-block}.xl\:bui-display-block{display:block}}.bui-w{width:var(--width)}.bui-min-w{min-width:var(--min-width)}.bui-max-w{max-width:var(--max-width)}@media (width>=640px){.xs\:bui-w{width:var(--width)}.xs\:bui-min-w{min-width:var(--min-width)}.xs\:bui-max-w{max-width:var(--max-width)}}@media (width>=768px){.sm\:bui-w{width:var(--width)}.sm\:bui-min-w{min-width:var(--min-width)}.sm\:bui-max-w{max-width:var(--max-width)}}@media (width>=1024px){.md\:bui-w{width:var(--width)}.md\:bui-min-w{min-width:var(--min-width)}.md\:bui-max-w{max-width:var(--max-width)}}@media (width>=1280px){.lg\:bui-w{width:var(--width)}.lg\:bui-min-w{min-width:var(--min-width)}.lg\:bui-max-w{max-width:var(--max-width)}}@media (width>=1536px){.xl\:bui-w{width:var(--width)}.xl\:bui-min-w{min-width:var(--min-width)}.xl\:bui-max-w{max-width:var(--max-width)}}.bui-h{height:var(--height)}.bui-min-h{min-height:var(--min-height)}.bui-max-h{max-height:var(--max-height)}@media (width>=640px){.xs\:bui-h{height:var(--height)}.xs\:bui-min-h{min-height:var(--min-height)}.xs\:bui-max-h{max-height:var(--max-height)}}@media (width>=768px){.sm\:bui-h{height:var(--height)}.sm\:bui-min-h{min-height:var(--min-height)}.sm\:bui-max-h{max-height:var(--max-height)}}@media (width>=1024px){.md\:bui-h{height:var(--height)}.md\:bui-min-h{min-height:var(--min-height)}.md\:bui-max-h{max-height:var(--max-height)}}@media (width>=1280px){.lg\:bui-h{height:var(--height)}.lg\:bui-min-h{min-height:var(--min-height)}.lg\:bui-max-h{max-height:var(--max-height)}}@media (width>=1536px){.xl\:bui-h{height:var(--height)}.xl\:bui-min-h{min-height:var(--min-height)}.xl\:bui-max-h{max-height:var(--max-height)}}.bui-position-absolute{position:absolute}.bui-position-fixed{position:fixed}.bui-position-sticky{position:sticky}.bui-position-relative{position:relative}.bui-position-static{position:static}@media (width>=640px){.xs\:bui-position-absolute{position:absolute}.xs\:bui-position-fixed{position:fixed}.xs\:bui-position-sticky{position:sticky}.xs\:bui-position-relative{position:relative}.xs\:bui-position-static{position:static}}@media (width>=768px){.sm\:bui-position-absolute{position:absolute}.sm\:bui-position-fixed{position:fixed}.sm\:bui-position-sticky{position:sticky}.sm\:bui-position-relative{position:relative}.sm\:bui-position-static{position:static}}@media (width>=1024px){.md\:bui-position-absolute{position:absolute}.md\:bui-position-fixed{position:fixed}.md\:bui-position-sticky{position:sticky}.md\:bui-position-relative{position:relative}.md\:bui-position-static{position:static}}@media (width>=1280px){.lg\:bui-position-absolute{position:absolute}.lg\:bui-position-fixed{position:fixed}.lg\:bui-position-sticky{position:sticky}.lg\:bui-position-relative{position:relative}.lg\:bui-position-static{position:static}}@media (width>=1536px){.xl\:bui-position-absolute{position:absolute}.xl\:bui-position-fixed{position:fixed}.xl\:bui-position-sticky{position:sticky}.xl\:bui-position-relative{position:relative}.xl\:bui-position-static{position:static}}.bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.bui-col-span-1{grid-column:span 1/span 1}.bui-col-span-2{grid-column:span 2/span 2}.bui-col-span-3{grid-column:span 3/span 3}.bui-col-span-4{grid-column:span 4/span 4}.bui-col-span-5{grid-column:span 5/span 5}.bui-col-span-6{grid-column:span 6/span 6}.bui-col-span-7{grid-column:span 7/span 7}.bui-col-span-8{grid-column:span 8/span 8}.bui-col-span-9{grid-column:span 9/span 9}.bui-col-span-10{grid-column:span 10/span 10}.bui-col-span-11{grid-column:span 11/span 11}.bui-col-span-12{grid-column:span 12/span 12}.bui-col-span-auto{grid-column:span auto/span auto}.bui-col-start-1{grid-column-start:1}.bui-col-start-2{grid-column-start:2}.bui-col-start-3{grid-column-start:3}.bui-col-start-4{grid-column-start:4}.bui-col-start-5{grid-column-start:5}.bui-col-start-6{grid-column-start:6}.bui-col-start-7{grid-column-start:7}.bui-col-start-8{grid-column-start:8}.bui-col-start-9{grid-column-start:9}.bui-col-start-10{grid-column-start:10}.bui-col-start-11{grid-column-start:11}.bui-col-start-12{grid-column-start:12}.bui-col-start-13{grid-column-start:13}.bui-col-start-auto{grid-column-start:auto}.bui-col-end-1{grid-column-end:1}.bui-col-end-2{grid-column-end:2}.bui-col-end-3{grid-column-end:3}.bui-col-end-4{grid-column-end:4}.bui-col-end-5{grid-column-end:5}.bui-col-end-6{grid-column-end:6}.bui-col-end-7{grid-column-end:7}.bui-col-end-8{grid-column-end:8}.bui-col-end-9{grid-column-end:9}.bui-col-end-10{grid-column-end:10}.bui-col-end-11{grid-column-end:11}.bui-col-end-12{grid-column-end:12}.bui-col-end-13{grid-column-end:13}.bui-col-end-auto{grid-column-end:auto}.bui-row-span-1{grid-row:span 1/span 1}.bui-row-span-2{grid-row:span 2/span 2}.bui-row-span-3{grid-row:span 3/span 3}.bui-row-span-4{grid-row:span 4/span 4}.bui-row-span-5{grid-row:span 5/span 5}.bui-row-span-6{grid-row:span 6/span 6}.bui-row-span-7{grid-row:span 7/span 7}.bui-row-span-8{grid-row:span 8/span 8}.bui-row-span-9{grid-row:span 9/span 9}.bui-row-span-10{grid-row:span 10/span 10}.bui-row-span-11{grid-row:span 11/span 11}.bui-row-span-12{grid-row:span 12/span 12}.bui-row-span-auto{grid-row:span auto/span auto}@media (width>=640px){.xs\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xs\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xs\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xs\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xs\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xs\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xs\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.xs\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.xs\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.xs\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xs\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.xs\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xs\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.xs\:bui-col-span-1{grid-column:span 1/span 1}.xs\:bui-col-span-2{grid-column:span 2/span 2}.xs\:bui-col-span-3{grid-column:span 3/span 3}.xs\:bui-col-span-4{grid-column:span 4/span 4}.xs\:bui-col-span-5{grid-column:span 5/span 5}.xs\:bui-col-span-6{grid-column:span 6/span 6}.xs\:bui-col-span-7{grid-column:span 7/span 7}.xs\:bui-col-span-8{grid-column:span 8/span 8}.xs\:bui-col-span-9{grid-column:span 9/span 9}.xs\:bui-col-span-10{grid-column:span 10/span 10}.xs\:bui-col-span-11{grid-column:span 11/span 11}.xs\:bui-col-span-12{grid-column:span 12/span 12}.xs\:bui-col-span-auto{grid-column:span auto/span auto}.xs\:bui-col-start-1{grid-column-start:1}.xs\:bui-col-start-2{grid-column-start:2}.xs\:bui-col-start-3{grid-column-start:3}.xs\:bui-col-start-4{grid-column-start:4}.xs\:bui-col-start-5{grid-column-start:5}.xs\:bui-col-start-6{grid-column-start:6}.xs\:bui-col-start-7{grid-column-start:7}.xs\:bui-col-start-8{grid-column-start:8}.xs\:bui-col-start-9{grid-column-start:9}.xs\:bui-col-start-10{grid-column-start:10}.xs\:bui-col-start-11{grid-column-start:11}.xs\:bui-col-start-12{grid-column-start:12}.xs\:bui-col-start-13{grid-column-start:13}.xs\:bui-col-start-auto{grid-column-start:auto}.xs\:bui-col-end-1{grid-column-end:1}.xs\:bui-col-end-2{grid-column-end:2}.xs\:bui-col-end-3{grid-column-end:3}.xs\:bui-col-end-4{grid-column-end:4}.xs\:bui-col-end-5{grid-column-end:5}.xs\:bui-col-end-6{grid-column-end:6}.xs\:bui-col-end-7{grid-column-end:7}.xs\:bui-col-end-8{grid-column-end:8}.xs\:bui-col-end-9{grid-column-end:9}.xs\:bui-col-end-10{grid-column-end:10}.xs\:bui-col-end-11{grid-column-end:11}.xs\:bui-col-end-12{grid-column-end:12}.xs\:bui-col-end-13{grid-column-end:13}.xs\:bui-col-end-auto{grid-column-end:auto}.xs\:bui-row-span-1{grid-row:span 1/span 1}.xs\:bui-row-span-2{grid-row:span 2/span 2}.xs\:bui-row-span-3{grid-row:span 3/span 3}.xs\:bui-row-span-4{grid-row:span 4/span 4}.xs\:bui-row-span-5{grid-row:span 5/span 5}.xs\:bui-row-span-6{grid-row:span 6/span 6}.xs\:bui-row-span-7{grid-row:span 7/span 7}.xs\:bui-row-span-8{grid-row:span 8/span 8}.xs\:bui-row-span-9{grid-row:span 9/span 9}.xs\:bui-row-span-10{grid-row:span 10/span 10}.xs\:bui-row-span-11{grid-row:span 11/span 11}.xs\:bui-row-span-12{grid-row:span 12/span 12}.xs\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=768px){.sm\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.sm\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.sm\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.sm\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.sm\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.sm\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.sm\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.sm\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.sm\:bui-col-span-1{grid-column:span 1/span 1}.sm\:bui-col-span-2{grid-column:span 2/span 2}.sm\:bui-col-span-3{grid-column:span 3/span 3}.sm\:bui-col-span-4{grid-column:span 4/span 4}.sm\:bui-col-span-5{grid-column:span 5/span 5}.sm\:bui-col-span-6{grid-column:span 6/span 6}.sm\:bui-col-span-7{grid-column:span 7/span 7}.sm\:bui-col-span-8{grid-column:span 8/span 8}.sm\:bui-col-span-9{grid-column:span 9/span 9}.sm\:bui-col-span-10{grid-column:span 10/span 10}.sm\:bui-col-span-11{grid-column:span 11/span 11}.sm\:bui-col-span-12{grid-column:span 12/span 12}.sm\:bui-col-span-auto{grid-column:span auto/span auto}.sm\:bui-col-start-1{grid-column-start:1}.sm\:bui-col-start-2{grid-column-start:2}.sm\:bui-col-start-3{grid-column-start:3}.sm\:bui-col-start-4{grid-column-start:4}.sm\:bui-col-start-5{grid-column-start:5}.sm\:bui-col-start-6{grid-column-start:6}.sm\:bui-col-start-7{grid-column-start:7}.sm\:bui-col-start-8{grid-column-start:8}.sm\:bui-col-start-9{grid-column-start:9}.sm\:bui-col-start-10{grid-column-start:10}.sm\:bui-col-start-11{grid-column-start:11}.sm\:bui-col-start-12{grid-column-start:12}.sm\:bui-col-start-13{grid-column-start:13}.sm\:bui-col-start-auto{grid-column-start:auto}.sm\:bui-col-end-1{grid-column-end:1}.sm\:bui-col-end-2{grid-column-end:2}.sm\:bui-col-end-3{grid-column-end:3}.sm\:bui-col-end-4{grid-column-end:4}.sm\:bui-col-end-5{grid-column-end:5}.sm\:bui-col-end-6{grid-column-end:6}.sm\:bui-col-end-7{grid-column-end:7}.sm\:bui-col-end-8{grid-column-end:8}.sm\:bui-col-end-9{grid-column-end:9}.sm\:bui-col-end-10{grid-column-end:10}.sm\:bui-col-end-11{grid-column-end:11}.sm\:bui-col-end-12{grid-column-end:12}.sm\:bui-col-end-13{grid-column-end:13}.sm\:bui-col-end-auto{grid-column-end:auto}.sm\:bui-row-span-1{grid-row:span 1/span 1}.sm\:bui-row-span-2{grid-row:span 2/span 2}.sm\:bui-row-span-3{grid-row:span 3/span 3}.sm\:bui-row-span-4{grid-row:span 4/span 4}.sm\:bui-row-span-5{grid-row:span 5/span 5}.sm\:bui-row-span-6{grid-row:span 6/span 6}.sm\:bui-row-span-7{grid-row:span 7/span 7}.sm\:bui-row-span-8{grid-row:span 8/span 8}.sm\:bui-row-span-9{grid-row:span 9/span 9}.sm\:bui-row-span-10{grid-row:span 10/span 10}.sm\:bui-row-span-11{grid-row:span 11/span 11}.sm\:bui-row-span-12{grid-row:span 12/span 12}.sm\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1024px){.md\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.md\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.md\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.md\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.md\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.md\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.md\:bui-col-span-1{grid-column:span 1/span 1}.md\:bui-col-span-2{grid-column:span 2/span 2}.md\:bui-col-span-3{grid-column:span 3/span 3}.md\:bui-col-span-4{grid-column:span 4/span 4}.md\:bui-col-span-5{grid-column:span 5/span 5}.md\:bui-col-span-6{grid-column:span 6/span 6}.md\:bui-col-span-7{grid-column:span 7/span 7}.md\:bui-col-span-8{grid-column:span 8/span 8}.md\:bui-col-span-9{grid-column:span 9/span 9}.md\:bui-col-span-10{grid-column:span 10/span 10}.md\:bui-col-span-11{grid-column:span 11/span 11}.md\:bui-col-span-12{grid-column:span 12/span 12}.md\:bui-col-span-auto{grid-column:span auto/span auto}.md\:bui-col-start-1{grid-column-start:1}.md\:bui-col-start-2{grid-column-start:2}.md\:bui-col-start-3{grid-column-start:3}.md\:bui-col-start-4{grid-column-start:4}.md\:bui-col-start-5{grid-column-start:5}.md\:bui-col-start-6{grid-column-start:6}.md\:bui-col-start-7{grid-column-start:7}.md\:bui-col-start-8{grid-column-start:8}.md\:bui-col-start-9{grid-column-start:9}.md\:bui-col-start-10{grid-column-start:10}.md\:bui-col-start-11{grid-column-start:11}.md\:bui-col-start-12{grid-column-start:12}.md\:bui-col-start-13{grid-column-start:13}.md\:bui-col-start-auto{grid-column-start:auto}.md\:bui-col-end-1{grid-column-end:1}.md\:bui-col-end-2{grid-column-end:2}.md\:bui-col-end-3{grid-column-end:3}.md\:bui-col-end-4{grid-column-end:4}.md\:bui-col-end-5{grid-column-end:5}.md\:bui-col-end-6{grid-column-end:6}.md\:bui-col-end-7{grid-column-end:7}.md\:bui-col-end-8{grid-column-end:8}.md\:bui-col-end-9{grid-column-end:9}.md\:bui-col-end-10{grid-column-end:10}.md\:bui-col-end-11{grid-column-end:11}.md\:bui-col-end-12{grid-column-end:12}.md\:bui-col-end-13{grid-column-end:13}.md\:bui-col-end-auto{grid-column-end:auto}.md\:bui-row-span-1{grid-row:span 1/span 1}.md\:bui-row-span-2{grid-row:span 2/span 2}.md\:bui-row-span-3{grid-row:span 3/span 3}.md\:bui-row-span-4{grid-row:span 4/span 4}.md\:bui-row-span-5{grid-row:span 5/span 5}.md\:bui-row-span-6{grid-row:span 6/span 6}.md\:bui-row-span-7{grid-row:span 7/span 7}.md\:bui-row-span-8{grid-row:span 8/span 8}.md\:bui-row-span-9{grid-row:span 9/span 9}.md\:bui-row-span-10{grid-row:span 10/span 10}.md\:bui-row-span-11{grid-row:span 11/span 11}.md\:bui-row-span-12{grid-row:span 12/span 12}.md\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1280px){.lg\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.lg\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.lg\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.lg\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.lg\:bui-col-span-1{grid-column:span 1/span 1}.lg\:bui-col-span-2{grid-column:span 2/span 2}.lg\:bui-col-span-3{grid-column:span 3/span 3}.lg\:bui-col-span-4{grid-column:span 4/span 4}.lg\:bui-col-span-5{grid-column:span 5/span 5}.lg\:bui-col-span-6{grid-column:span 6/span 6}.lg\:bui-col-span-7{grid-column:span 7/span 7}.lg\:bui-col-span-8{grid-column:span 8/span 8}.lg\:bui-col-span-9{grid-column:span 9/span 9}.lg\:bui-col-span-10{grid-column:span 10/span 10}.lg\:bui-col-span-11{grid-column:span 11/span 11}.lg\:bui-col-span-12{grid-column:span 12/span 12}.lg\:bui-col-span-auto{grid-column:span auto/span auto}.lg\:bui-col-start-1{grid-column-start:1}.lg\:bui-col-start-2{grid-column-start:2}.lg\:bui-col-start-3{grid-column-start:3}.lg\:bui-col-start-4{grid-column-start:4}.lg\:bui-col-start-5{grid-column-start:5}.lg\:bui-col-start-6{grid-column-start:6}.lg\:bui-col-start-7{grid-column-start:7}.lg\:bui-col-start-8{grid-column-start:8}.lg\:bui-col-start-9{grid-column-start:9}.lg\:bui-col-start-10{grid-column-start:10}.lg\:bui-col-start-11{grid-column-start:11}.lg\:bui-col-start-12{grid-column-start:12}.lg\:bui-col-start-13{grid-column-start:13}.lg\:bui-col-start-auto{grid-column-start:auto}.lg\:bui-col-end-1{grid-column-end:1}.lg\:bui-col-end-2{grid-column-end:2}.lg\:bui-col-end-3{grid-column-end:3}.lg\:bui-col-end-4{grid-column-end:4}.lg\:bui-col-end-5{grid-column-end:5}.lg\:bui-col-end-6{grid-column-end:6}.lg\:bui-col-end-7{grid-column-end:7}.lg\:bui-col-end-8{grid-column-end:8}.lg\:bui-col-end-9{grid-column-end:9}.lg\:bui-col-end-10{grid-column-end:10}.lg\:bui-col-end-11{grid-column-end:11}.lg\:bui-col-end-12{grid-column-end:12}.lg\:bui-col-end-13{grid-column-end:13}.lg\:bui-col-end-auto{grid-column-end:auto}.lg\:bui-row-span-1{grid-row:span 1/span 1}.lg\:bui-row-span-2{grid-row:span 2/span 2}.lg\:bui-row-span-3{grid-row:span 3/span 3}.lg\:bui-row-span-4{grid-row:span 4/span 4}.lg\:bui-row-span-5{grid-row:span 5/span 5}.lg\:bui-row-span-6{grid-row:span 6/span 6}.lg\:bui-row-span-7{grid-row:span 7/span 7}.lg\:bui-row-span-8{grid-row:span 8/span 8}.lg\:bui-row-span-9{grid-row:span 9/span 9}.lg\:bui-row-span-10{grid-row:span 10/span 10}.lg\:bui-row-span-11{grid-row:span 11/span 11}.lg\:bui-row-span-12{grid-row:span 12/span 12}.lg\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1536px){.xl\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xl\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.xl\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.xl\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.xl\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.xl\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xl\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.xl\:bui-col-span-1{grid-column:span 1/span 1}.xl\:bui-col-span-2{grid-column:span 2/span 2}.xl\:bui-col-span-3{grid-column:span 3/span 3}.xl\:bui-col-span-4{grid-column:span 4/span 4}.xl\:bui-col-span-5{grid-column:span 5/span 5}.xl\:bui-col-span-6{grid-column:span 6/span 6}.xl\:bui-col-span-7{grid-column:span 7/span 7}.xl\:bui-col-span-8{grid-column:span 8/span 8}.xl\:bui-col-span-9{grid-column:span 9/span 9}.xl\:bui-col-span-10{grid-column:span 10/span 10}.xl\:bui-col-span-11{grid-column:span 11/span 11}.xl\:bui-col-span-12{grid-column:span 12/span 12}.xl\:bui-col-span-auto{grid-column:span auto/span auto}.xl\:bui-col-start-1{grid-column-start:1}.xl\:bui-col-start-2{grid-column-start:2}.xl\:bui-col-start-3{grid-column-start:3}.xl\:bui-col-start-4{grid-column-start:4}.xl\:bui-col-start-5{grid-column-start:5}.xl\:bui-col-start-6{grid-column-start:6}.xl\:bui-col-start-7{grid-column-start:7}.xl\:bui-col-start-8{grid-column-start:8}.xl\:bui-col-start-9{grid-column-start:9}.xl\:bui-col-start-10{grid-column-start:10}.xl\:bui-col-start-11{grid-column-start:11}.xl\:bui-col-start-12{grid-column-start:12}.xl\:bui-col-start-13{grid-column-start:13}.xl\:bui-col-start-auto{grid-column-start:auto}.xl\:bui-col-end-1{grid-column-end:1}.xl\:bui-col-end-2{grid-column-end:2}.xl\:bui-col-end-3{grid-column-end:3}.xl\:bui-col-end-4{grid-column-end:4}.xl\:bui-col-end-5{grid-column-end:5}.xl\:bui-col-end-6{grid-column-end:6}.xl\:bui-col-end-7{grid-column-end:7}.xl\:bui-col-end-8{grid-column-end:8}.xl\:bui-col-end-9{grid-column-end:9}.xl\:bui-col-end-10{grid-column-end:10}.xl\:bui-col-end-11{grid-column-end:11}.xl\:bui-col-end-12{grid-column-end:12}.xl\:bui-col-end-13{grid-column-end:13}.xl\:bui-col-end-auto{grid-column-end:auto}.xl\:bui-row-span-1{grid-row:span 1/span 1}.xl\:bui-row-span-2{grid-row:span 2/span 2}.xl\:bui-row-span-3{grid-row:span 3/span 3}.xl\:bui-row-span-4{grid-row:span 4/span 4}.xl\:bui-row-span-5{grid-row:span 5/span 5}.xl\:bui-row-span-6{grid-row:span 6/span 6}.xl\:bui-row-span-7{grid-row:span 7/span 7}.xl\:bui-row-span-8{grid-row:span 8/span 8}.xl\:bui-row-span-9{grid-row:span 9/span 9}.xl\:bui-row-span-10{grid-row:span 10/span 10}.xl\:bui-row-span-11{grid-row:span 11/span 11}.xl\:bui-row-span-12{grid-row:span 12/span 12}.xl\:bui-row-span-auto{grid-row:span auto/span auto}}.bui-gap{gap:var(--gap)}.bui-gap-0\.5{gap:var(--bui-space-0_5)}.bui-gap-1{gap:var(--bui-space-1)}.bui-gap-1\.5{gap:var(--bui-space-1_5)}.bui-gap-2{gap:var(--bui-space-2)}.bui-gap-3{gap:var(--bui-space-3)}.bui-gap-4{gap:var(--bui-space-4)}.bui-gap-5{gap:var(--bui-space-5)}.bui-gap-6{gap:var(--bui-space-6)}.bui-gap-7{gap:var(--bui-space-7)}.bui-gap-8{gap:var(--bui-space-8)}.bui-gap-9{gap:var(--bui-space-9)}.bui-gap-10{gap:var(--bui-space-10)}.bui-gap-11{gap:var(--bui-space-11)}.bui-gap-12{gap:var(--bui-space-12)}.bui-gap-13{gap:var(--bui-space-13)}.bui-gap-14{gap:var(--bui-space-14)}@media (width>=640px){.xs\:bui-gap{gap:var(--gap-xs)}.xs\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.xs\:bui-gap-1{gap:var(--bui-space-1)}.xs\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.xs\:bui-gap-2{gap:var(--bui-space-2)}.xs\:bui-gap-3{gap:var(--bui-space-3)}.xs\:bui-gap-4{gap:var(--bui-space-4)}.xs\:bui-gap-5{gap:var(--bui-space-5)}.xs\:bui-gap-6{gap:var(--bui-space-6)}.xs\:bui-gap-7{gap:var(--bui-space-7)}.xs\:bui-gap-8{gap:var(--bui-space-8)}.xs\:bui-gap-9{gap:var(--bui-space-9)}.xs\:bui-gap-10{gap:var(--bui-space-10)}.xs\:bui-gap-11{gap:var(--bui-space-11)}.xs\:bui-gap-12{gap:var(--bui-space-12)}.xs\:bui-gap-13{gap:var(--bui-space-13)}.xs\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-gap{gap:var(--gap-sm)}.sm\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.sm\:bui-gap-1{gap:var(--bui-space-1)}.sm\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.sm\:bui-gap-2{gap:var(--bui-space-2)}.sm\:bui-gap-3{gap:var(--bui-space-3)}.sm\:bui-gap-4{gap:var(--bui-space-4)}.sm\:bui-gap-5{gap:var(--bui-space-5)}.sm\:bui-gap-6{gap:var(--bui-space-6)}.sm\:bui-gap-7{gap:var(--bui-space-7)}.sm\:bui-gap-8{gap:var(--bui-space-8)}.sm\:bui-gap-9{gap:var(--bui-space-9)}.sm\:bui-gap-10{gap:var(--bui-space-10)}.sm\:bui-gap-11{gap:var(--bui-space-11)}.sm\:bui-gap-12{gap:var(--bui-space-12)}.sm\:bui-gap-13{gap:var(--bui-space-13)}.sm\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-gap{gap:var(--gap-md)}.md\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.md\:bui-gap-1{gap:var(--bui-space-1)}.md\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.md\:bui-gap-2{gap:var(--bui-space-2)}.md\:bui-gap-3{gap:var(--bui-space-3)}.md\:bui-gap-4{gap:var(--bui-space-4)}.md\:bui-gap-5{gap:var(--bui-space-5)}.md\:bui-gap-6{gap:var(--bui-space-6)}.md\:bui-gap-7{gap:var(--bui-space-7)}.md\:bui-gap-8{gap:var(--bui-space-8)}.md\:bui-gap-9{gap:var(--bui-space-9)}.md\:bui-gap-10{gap:var(--bui-space-10)}.md\:bui-gap-11{gap:var(--bui-space-11)}.md\:bui-gap-12{gap:var(--bui-space-12)}.md\:bui-gap-13{gap:var(--bui-space-13)}.md\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-gap{gap:var(--gap-lg)}.lg\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.lg\:bui-gap-1{gap:var(--bui-space-1)}.lg\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.lg\:bui-gap-2{gap:var(--bui-space-2)}.lg\:bui-gap-3{gap:var(--bui-space-3)}.lg\:bui-gap-4{gap:var(--bui-space-4)}.lg\:bui-gap-5{gap:var(--bui-space-5)}.lg\:bui-gap-6{gap:var(--bui-space-6)}.lg\:bui-gap-7{gap:var(--bui-space-7)}.lg\:bui-gap-8{gap:var(--bui-space-8)}.lg\:bui-gap-9{gap:var(--bui-space-9)}.lg\:bui-gap-10{gap:var(--bui-space-10)}.lg\:bui-gap-11{gap:var(--bui-space-11)}.lg\:bui-gap-12{gap:var(--bui-space-12)}.lg\:bui-gap-13{gap:var(--bui-space-13)}.lg\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-gap{gap:var(--gap-xl)}.xl\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.xl\:bui-gap-1{gap:var(--bui-space-1)}.xl\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.xl\:bui-gap-2{gap:var(--bui-space-2)}.xl\:bui-gap-3{gap:var(--bui-space-3)}.xl\:bui-gap-4{gap:var(--bui-space-4)}.xl\:bui-gap-5{gap:var(--bui-space-5)}.xl\:bui-gap-6{gap:var(--bui-space-6)}.xl\:bui-gap-7{gap:var(--bui-space-7)}.xl\:bui-gap-8{gap:var(--bui-space-8)}.xl\:bui-gap-9{gap:var(--bui-space-9)}.xl\:bui-gap-10{gap:var(--bui-space-10)}.xl\:bui-gap-11{gap:var(--bui-space-11)}.xl\:bui-gap-12{gap:var(--bui-space-12)}.xl\:bui-gap-13{gap:var(--bui-space-13)}.xl\:bui-gap-14{gap:var(--bui-space-14)}}.bui-align-start{align-items:start}.bui-align-center{align-items:center}.bui-align-end{align-items:end}.bui-align-baseline{align-items:baseline}.bui-align-stretch{align-items:stretch}.bui-jc-start{justify-content:start}.bui-jc-center{justify-content:center}.bui-jc-end{justify-content:end}.bui-jc-between{justify-content:space-between}.bui-fd-row{flex-direction:row}.bui-fd-column{flex-direction:column}.bui-fd-row-reverse{flex-direction:row-reverse}.bui-fd-column-reverse{flex-direction:column-reverse}@media (width>=640px){.xs\:bui-align-start{align-items:start}.xs\:bui-align-center{align-items:center}.xs\:bui-align-end{align-items:end}.xs\:bui-align-stretch{align-items:stretch}.xs\:bui-jc-start{justify-content:start}.xs\:bui-jc-center{justify-content:center}.xs\:bui-jc-end{justify-content:end}.xs\:bui-jc-between{justify-content:space-between}.xs\:bui-fd-row{flex-direction:row}.xs\:bui-fd-column{flex-direction:column}.xs\:bui-fd-row-reverse{flex-direction:row-reverse}.xs\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=768px){.sm\:bui-align-start{align-items:start}.sm\:bui-align-center{align-items:center}.sm\:bui-align-end{align-items:end}.sm\:bui-align-stretch{align-items:stretch}.sm\:bui-jc-start{justify-content:start}.sm\:bui-jc-center{justify-content:center}.sm\:bui-jc-end{justify-content:end}.sm\:bui-jc-between{justify-content:space-between}.sm\:bui-fd-row{flex-direction:row}.sm\:bui-fd-column{flex-direction:column}.sm\:bui-fd-row-reverse{flex-direction:row-reverse}.sm\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1024px){.md\:bui-align-start{align-items:start}.md\:bui-align-center{align-items:center}.md\:bui-align-end{align-items:end}.md\:bui-align-stretch{align-items:stretch}.md\:bui-jc-start{justify-content:start}.md\:bui-jc-center{justify-content:center}.md\:bui-jc-end{justify-content:end}.md\:bui-jc-between{justify-content:space-between}.md\:bui-fd-row{flex-direction:row}.md\:bui-fd-column{flex-direction:column}.md\:bui-fd-row-reverse{flex-direction:row-reverse}.md\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1280px){.lg\:bui-align-start{align-items:start}.lg\:bui-align-center{align-items:center}.lg\:bui-align-end{align-items:end}.lg\:bui-align-stretch{align-items:stretch}.lg\:bui-jc-start{justify-content:start}.lg\:bui-jc-center{justify-content:center}.lg\:bui-jc-end{justify-content:end}.lg\:bui-jc-between{justify-content:space-between}.lg\:bui-fd-row{flex-direction:row}.lg\:bui-fd-column{flex-direction:column}.lg\:bui-fd-row-reverse{flex-direction:row-reverse}.lg\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1536px){.xl\:bui-align-start{align-items:start}.xl\:bui-align-center{align-items:center}.xl\:bui-align-end{align-items:end}.xl\:bui-align-stretch{align-items:stretch}.xl\:bui-jc-start{justify-content:start}.xl\:bui-jc-center{justify-content:center}.xl\:bui-jc-end{justify-content:end}.xl\:bui-jc-between{justify-content:space-between}.xl\:bui-fd-row{flex-direction:row}.xl\:bui-fd-column{flex-direction:column}.xl\:bui-fd-row-reverse{flex-direction:row-reverse}.xl\:bui-fd-column-reverse{flex-direction:column-reverse}}}
\ No newline at end of file
diff --git a/docs-ui/public/theme-spotify.css b/docs-ui/public/theme-spotify.css
new file mode 100644
index 0000000000..b0bf517701
--- /dev/null
+++ b/docs-ui/public/theme-spotify.css
@@ -0,0 +1 @@
+@font-face{font-family:CircularSpTitle;font-weight:900;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSpTitle-Black-4588c99025b967475c31695b0743dd1a.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSpTitle-Black-506746f387a26f25aa3d023b3e501d34.woff)format("woff")}@font-face{font-family:CircularSpTitle;font-weight:700;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSpTitle-Bold-b2586b06a2e1522e9d879d84c2792a58.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSpTitle-Bold-1624fb2df28c20d7203d7fb86ce8b481.woff)format("woff")}@font-face{font-family:CircularSp;font-weight:700;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff)format("woff")}@font-face{font-family:CircularSp;font-weight:400;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSp-Book-a00e99ef9996a3a157fb6b746856d04f.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSp-Book-3f73da7d35bd81c706bce7bbb84964de.woff)format("woff")}@font-face{font-family:CircularSp;font-weight:700;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff)format("woff")}[data-theme-name=spotify]{--bui-font-text:CircularSp,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;--bui-font-title:CircularSpTitle,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;--bui-font-regular:CircularSp,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;& .bui-Button{border-radius:var(--bui-radius-3);padding-inline:var(--bui-space-3)}& .bui-ButtonIcon{padding:0}& .bui-MenuPopup{box-sizing:border-box;max-width:21.25rem}& .bui-MenuSubmenuTrigger,& .bui-MenuItem{height:auto;min-height:2rem}& .bui-Text{font-family:var(--bui-font-text)}& .bui-Heading{font-family:var(--bui-font-title)}& .bui-TableRow{border-radius:var(--bui-radius-4);border:none}& .bui-TableRow:hover td:first-child{border-top-left-radius:var(--bui-radius-2);border-bottom-left-radius:var(--bui-radius-2)}& .bui-TableRow:hover td:last-child{border-top-right-radius:var(--bui-radius-2);border-bottom-right-radius:var(--bui-radius-2)}& .bui-TableBody:before,& .bui-TableBody:after{line-height:var(--bui-space-1);content:"";display:block}& .bui-TableHeader .bui-TableRow{border-bottom:1px solid var(--bui-border)}& .bui-TableHead{font-size:var(--bui-font-size-2);color:var(--bui-fg-secondary);font-weight:var(--bui-font-weight-regular)}& .bui-HeaderToolbar{padding-top:var(--bui-space-2);padding-inline:var(--bui-space-2)}& .bui-HeaderToolbarWrapper{border-radius:var(--bui-radius-3);padding-inline:var(--bui-space-3);border:none}& .bui-HeaderToolbarControls{right:calc(var(--bui-space-3) - 1px)}& .bui-HeaderTabsWrapper{margin-top:var(--bui-space-2);margin-inline:var(--bui-space-2);border-radius:var(--bui-radius-3);padding-inline:var(--bui-space-1);border:none}& .bui-Input{border-radius:var(--bui-radius-3)}& .bui-Tag{border-radius:var(--bui-radius-full)}}[data-theme-mode=light][data-theme-name=spotify]{--bui-bg:var(--bui-gray-1);--bui-bg-surface-1:var(--bui-white);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#1ed760;--bui-bg-solid-hover:#3be477;--bui-bg-solid-pressed:#1abc54;--bui-bg-solid-disabled:var(--bui-gray-2);--bui-fg-primary:var(--bui-black);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-solid:var(--bui-black);--bui-border:var(--bui-gray-3);--bui-border-hover:#0000004d;--bui-border-pressed:#00000080;--bui-border-disabled:#0000001a;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#0003;& .bui-HeaderToolbarWrapper,& .bui-HeaderTabsWrapper{border:1px solid var(--bui-border)}}[data-theme-mode=dark][data-theme-name=spotify]{--bui-bg:var(--bui-black);--bui-bg-surface-1:var(--bui-gray-1);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#1ed760;--bui-bg-solid-hover:#3be477;--bui-bg-solid-pressed:#1abc54;--bui-bg-solid-disabled:#0f6c30;--bui-bg-tint:#242424;--bui-bg-tint-hover:#202020;--bui-bg-tint-pressed:#292929;--bui-bg-tint-disabled:#fffc;--bui-bg-danger:#3b1219;--bui-bg-warning:#302008;--bui-bg-success:#132d21;--bui-fg-primary:var(--bui-white);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:var(--bui-white);--bui-fg-link-hover:var(--bui-white);--bui-fg-disabled:var(--bui-gray-5);--bui-fg-solid:var(--bui-black);--bui-fg-solid-disabled:#083618;--bui-fg-tint:var(--bui-white);--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:var(--bui-gray-3);--bui-border-hover:#fff6;--bui-border-pressed:#ffffff80;--bui-border-disabled:#fff3;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#fff3}
\ No newline at end of file
diff --git a/docs-ui/scripts/sync-css.js b/docs-ui/scripts/sync-css.js
new file mode 100644
index 0000000000..4763fdecc1
--- /dev/null
+++ b/docs-ui/scripts/sync-css.js
@@ -0,0 +1,149 @@
+const fs = require('fs');
+const path = require('path');
+const { bundle } = require('lightningcss');
+const chokidar = require('chokidar');
+
+// Configuration
+const config = {
+ UIPath: '../../packages/ui',
+ publicPath: '../public',
+ files: [
+ {
+ source: 'css/styles.css',
+ destination: 'theme-backstage.css',
+ name: 'Main Styles',
+ },
+ {
+ source: '../../.storybook/themes/spotify.css',
+ destination: 'theme-spotify.css',
+ name: 'Spotify Theme',
+ },
+ ],
+};
+
+class CSSSync {
+ constructor() {
+ this.UIPath = path.resolve(__dirname, config.UIPath);
+ this.publicPath = path.resolve(__dirname, config.publicPath);
+ this.isWatching = process.argv.includes('--watch');
+ }
+
+ async syncFile(fileConfig) {
+ const sourcePath = path.join(this.UIPath, fileConfig.source);
+ const destPath = path.join(this.publicPath, fileConfig.destination);
+
+ try {
+ // Check if source file exists
+ if (!fs.existsSync(sourcePath)) {
+ console.warn(`⚠️ Source file not found: ${sourcePath}`);
+ return false;
+ }
+
+ // Ensure destination directory exists
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
+
+ // Bundle and optimize CSS
+ const result = await bundle({
+ filename: sourcePath,
+ minify: true,
+ });
+
+ // Write to destination
+ fs.writeFileSync(destPath, result.code);
+
+ console.log(
+ `✅ ${fileConfig.name}: ${fileConfig.source} → ${fileConfig.destination}`,
+ );
+ return true;
+ } catch (error) {
+ console.error(`❌ Error syncing ${fileConfig.name}:`, error.message);
+ return false;
+ }
+ }
+
+ async syncAll() {
+ console.log('🔄 Syncing CSS files...\n');
+
+ let successCount = 0;
+ for (const fileConfig of config.files) {
+ if (await this.syncFile(fileConfig)) {
+ successCount++;
+ }
+ }
+
+ console.log(
+ `\n✨ Synced ${successCount}/${config.files.length} CSS files successfully!`,
+ );
+
+ if (successCount > 0) {
+ console.log('\n📁 Available CSS files in public/:');
+ config.files.forEach(file => {
+ const destPath = path.join(this.publicPath, file.destination);
+ if (fs.existsSync(destPath)) {
+ const stats = fs.statSync(destPath);
+ const size = (stats.size / 1024).toFixed(2);
+ console.log(` • ${file.destination} (${size} KB)`);
+ }
+ });
+ }
+ }
+
+ startWatching() {
+ console.log('👀 Watching for CSS changes...\n');
+
+ // Watch all source files
+ const watchPaths = config.files.map(file =>
+ path.join(this.UIPath, file.source),
+ );
+
+ const watcher = chokidar.watch(watchPaths, {
+ ignored: /node_modules/,
+ persistent: true,
+ });
+
+ watcher.on('change', async filePath => {
+ console.log(
+ `\n🔄 Change detected: ${path.relative(this.UIPath, filePath)}`,
+ );
+
+ // Find which file changed and sync it
+ const fileConfig = config.files.find(file =>
+ filePath.endsWith(file.source.replace(/\//g, path.sep)),
+ );
+
+ if (fileConfig) {
+ await this.syncFile(fileConfig);
+ }
+ });
+
+ watcher.on('error', error => console.error('❌ Watch error:', error));
+
+ // Handle process termination
+ process.on('SIGINT', () => {
+ console.log('\n👋 Stopping CSS sync...');
+ watcher.close();
+ process.exit(0);
+ });
+ }
+
+ async run() {
+ console.log('🎨 BUI CSS Sync Tool\n');
+ console.log(`📂 BUI path: ${this.UIPath}`);
+ console.log(`📂 Public path: ${this.publicPath}\n`);
+
+ // Initial sync
+ await this.syncAll();
+
+ // Watch for changes if requested
+ if (this.isWatching) {
+ this.startWatching();
+ }
+ }
+}
+
+// Run the sync tool
+const cssSync = new CSSSync();
+cssSync.run().catch(error => {
+ console.error('❌ CSS Sync failed:', error);
+ process.exit(1);
+});
diff --git a/docs-ui/src/app/changelog/page.mdx b/docs-ui/src/app/changelog/page.mdx
new file mode 100644
index 0000000000..bbeaa98458
--- /dev/null
+++ b/docs-ui/src/app/changelog/page.mdx
@@ -0,0 +1,13 @@
+import { Changelog } from '@/components/Changelog';
+
+# Changelog
+
+
+
+## Version 0.1.0
+
+We're excited to share the initial release of Backstage UI 💚 In this first alpha version,
+you'll find the foundation of our design system: a set of versatile layout components
+and a handful of essential atomic elements to help you get started. While Backstage UI is
+still in its early stages, it's ready for exploration and we'd love for you to give
+it a try and share your feedback.
diff --git a/docs-ui/src/app/components/[slug]/page.tsx b/docs-ui/src/app/components/[slug]/page.tsx
new file mode 100644
index 0000000000..1422334471
--- /dev/null
+++ b/docs-ui/src/app/components/[slug]/page.tsx
@@ -0,0 +1,23 @@
+import { components, layoutComponents } from '@/utils/data';
+
+export default async function Page({
+ params,
+}: {
+ params: Promise<{ slug: string }>;
+}) {
+ const { slug } = await params;
+
+ const { default: Component } = await import(`@/content/${slug}.mdx`);
+
+ return ;
+}
+
+export function generateStaticParams() {
+ const list = [...components, ...layoutComponents];
+
+ return list.map(component => ({
+ slug: component.slug,
+ }));
+}
+
+export const dynamicParams = false;
diff --git a/docs-ui/src/app/components/page.mdx b/docs-ui/src/app/components/page.mdx
new file mode 100644
index 0000000000..bd18005d19
--- /dev/null
+++ b/docs-ui/src/app/components/page.mdx
@@ -0,0 +1,173 @@
+import { ComponentCards, ComponentCard } from '@/components/ComponentCards';
+import { LayoutComponents } from '@/components/LayoutComponents';
+import { CodeBlock } from '@/components/CodeBlock';
+
+# Components
+
+## Layout Components
+
+We built a couple of layout components to help you build responsive elements
+that will be consistent with the rest of your Backstage instance. These
+components are opinionated and use TypeScript to ensure that the props you
+provide are the ones coming from the theme.
+
+
+ Hello World
+
+ Project 1
+ Project 2
+
+
+`}
+/>
+
+
+
+## Components
+
+### Actions
+
+
+
+
+
+
+
+
+### Content display
+
+
+
+
+
+
+### Selection and inputs
+
+
+
+
+
+
+
+
+
+
+
+### Navigation
+
+
+
+
+
+
+
+
+### Images and icons
+
+
+
+
+
+
+### Feedback indicators
+
+
+
+
+
+
+### Typography
+
+
+
+
diff --git a/docs-ui/src/app/icon.svg b/docs-ui/src/app/icon.svg
new file mode 100644
index 0000000000..b50d29e902
--- /dev/null
+++ b/docs-ui/src/app/icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs-ui/src/app/layout.module.css b/docs-ui/src/app/layout.module.css
new file mode 100644
index 0000000000..df89f02283
--- /dev/null
+++ b/docs-ui/src/app/layout.module.css
@@ -0,0 +1,46 @@
+.container {
+ position: relative;
+ z-index: 20;
+ margin-bottom: 48px;
+ padding-inline: 24px;
+}
+
+.contentWrapper {
+ display: flex;
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+ justify-content: center;
+ flex-direction: column;
+}
+
+.content {
+ display: flex;
+ gap: 40px;
+}
+
+.contentInner {
+ flex: 1;
+ min-width: 0;
+ overflow-x: hidden;
+}
+
+.toc {
+ display: none;
+}
+
+@media (min-width: 768px) {
+ .container {
+ width: calc(100% - 260px);
+ margin-left: 260px;
+ margin-right: 40px;
+ }
+}
+
+@media (min-width: 1280px) {
+ .toc {
+ display: block;
+ width: 240px;
+ flex-shrink: 0;
+ }
+}
diff --git a/docs-ui/src/app/layout.tsx b/docs-ui/src/app/layout.tsx
new file mode 100644
index 0000000000..ad056626d8
--- /dev/null
+++ b/docs-ui/src/app/layout.tsx
@@ -0,0 +1,72 @@
+import type { Metadata } from 'next';
+import { Sidebar } from '@/components/Sidebar/Sidebar';
+import { Toolbar } from '@/components/Toolbar';
+import { Providers } from './providers';
+import { CustomTheme } from '@/components/CustomTheme';
+import { TableOfContents } from '@/components/TableOfContents';
+import { MobileBottomNav } from '@/components/MobileBottomNav';
+import styles from './layout.module.css';
+
+import '../css/globals.css';
+import '/public/theme-backstage.css';
+import '/public/theme-spotify.css';
+
+export const metadata: Metadata = {
+ title: 'Backstage UI',
+ description: 'UI library for Backstage',
+ metadataBase: new URL('https://ui.backstage.io'),
+};
+
+async function getLatestPackageVersion(): Promise {
+ try {
+ const response = await fetch('https://registry.npmjs.org/@backstage/ui', {
+ next: { revalidate: 3600 }, // Cache for 1 hour
+ });
+
+ if (!response.ok) {
+ throw new Error('Failed to fetch package info');
+ }
+
+ const data = await response.json();
+ return data['dist-tags']?.latest;
+ } catch (error) {
+ console.error('Error fetching package version:', error);
+ return '0.0.0';
+ }
+}
+
+export default async function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ const packageVersion = await getLatestPackageVersion();
+
+ return (
+
+
+
+
+
+
+
+
+
{children}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/docs-ui/src/app/opengraph-image.jpg b/docs-ui/src/app/opengraph-image.jpg
new file mode 100644
index 0000000000..86546a32ee
Binary files /dev/null and b/docs-ui/src/app/opengraph-image.jpg differ
diff --git a/docs-ui/src/app/page.mdx b/docs-ui/src/app/page.mdx
new file mode 100644
index 0000000000..9a796b401b
--- /dev/null
+++ b/docs-ui/src/app/page.mdx
@@ -0,0 +1,83 @@
+import { CodeBlock } from '@/components/CodeBlock';
+import { Banner } from '@/components/Banner';
+import { snippet } from './snippets';
+
+# Welcome to Backstage UI
+
+Backstage UI is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS.
+This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Backstage UI
+is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users.
+
+## Import BUI's global styles
+
+Backstage UI works by importing a global CSS file at the root of your application. This file includes all the default styles for the components.
+First, you'll need to install the package using a package manager. For example, if you're using Yarn:
+
+
+
+);`}
+/>
+
+
+
+## Use BUI components
+
+As a plugin maintainer, you can use BUI components in your plugin. As mentioned above, you should not import the styles
+again in your plugin as this will be handled at the root of your application. To get started, just add the library to
+your plugin and import the components you need.
+
+
+
+
+
+## Support
+
+Now that you have the basics down, you can start building your plugin using the new design system.
+Please familiarise yourself first with our theming principles. This will help you understand the core concepts of the design system.
+If you have any questions, please reach out to us on [Discord](https://discord.gg/MUpMjP2).
+
+## Philosophy
+
+Backstage empowers product teams to build software faster and with greater quality. Its extensibility,
+however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is
+to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while
+still allowing customization to match individual brands.
+
+Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless
+component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive
+and flexible theming system.
+
+## Team
+
+Backstage UI is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in
+crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive
+user experiences for the music industry, we've created a design system that looks great and works seamlessly.
+
+## Community
+
+Backstage UI is an open-source project and we welcome contributions from the community. If you are interested in
+contributing to Backstage UI, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)
+and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md).
+
+## License
+
+Backstage UI is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details.
diff --git a/canon-docs/src/app/providers.tsx b/docs-ui/src/app/providers.tsx
similarity index 100%
rename from canon-docs/src/app/providers.tsx
rename to docs-ui/src/app/providers.tsx
diff --git a/docs-ui/src/app/snippets.ts b/docs-ui/src/app/snippets.ts
new file mode 100644
index 0000000000..7e63005172
--- /dev/null
+++ b/docs-ui/src/app/snippets.ts
@@ -0,0 +1,6 @@
+export const snippet = `import { Flex, Button, Text } from '@backstage/ui';
+
+
+ Hello World
+
+;`;
diff --git a/canon-docs/src/app/(docs)/theme/theming/page.mdx b/docs-ui/src/app/tokens/page.mdx
similarity index 63%
rename from canon-docs/src/app/(docs)/theme/theming/page.mdx
rename to docs-ui/src/app/tokens/page.mdx
index ed7f801db0..8e9031c81c 100644
--- a/canon-docs/src/app/(docs)/theme/theming/page.mdx
+++ b/docs-ui/src/app/tokens/page.mdx
@@ -3,48 +3,109 @@ import * as Table from '@/components/Table';
import { Chip } from '@/components/Chip';
import { customTheme } from '@/snippets/code-snippets';
-# Theming
+# Tokens
-Canon's theming is built entirely on CSS, without relying on any CSS-in-JS libraries.
-At its core, it provides a solid default theme that is easily customizable using a
-comprehensive set of CSS variables. Additionally, it enables anyone to adapt the design
-to their specific needs. Each component comes with fixed class names, making customization
-even more straightforward.
+## Responsive breakpoints
-## Light & Dark modes
+Backstage UI is built on a responsive design system, meaning that the components are
+designed to adapt to different screen sizes. By default we offer a set of
+breakpoints that you can use to create responsive components.
-By default, Canon supports both light and dark modes using the `data-theme` attribute.
-The light theme is applied by default if no `data-theme` attribute is specified. To create
-a custom theme, you'll need to define both light and dark modes as outlined below. If
-only one mode is defined, the other will fall back to the default theme.
+
+
+
+ Breakpoint prefix
+ Minimum width
+ CSS
+
+
+
+
+
+ xs
+
+
+ 0px
+
+
+ {`{ ... }`}
+
+
+
+
+ sm
+
+
+ 640px
+
+
+ {`@media (min-width: 640px) { ... }`}
+
+
+
+
+ md
+
+
+ 768px
+
+
+ {`@media (min-width: 768px) { ... }`}
+
+
+
+
+ lg
+
+
+ 1024px
+
+
+ {`@media (min-width: 1024px) { ... }`}
+
+
+
+
+ xl
+
+
+ 1280px
+
+
+ {`@media (min-width: 1280px) { ... }`}
+
+
+
+
+ 2xl
+
+
+ 1536px
+
+
+ {`@media (min-width: 1536px) { ... }`}
+
+
+
+
-## How to create your own theme
+Backstage UI components are designed to be responsive, meaning that they will adapt
+to different screen sizes. Not every component is responsive, but the ones
+that are will have a prop to control the responsive behavior.
-In our [started guide](/), we ask you to import two css files. The `core.css` file includes
-the default set of variables. We recommend to keep this file in place and add your own theme
-on top of it. `core.css` also include an opinionated reset. If you decided to remove `core.css`
-you will have to provide your own reset css.
+The behaviour is the same for each component. For each prop, instead of adding
+the value, you add an object with the value and the breakpoint prefix.
-Here's an example of how your theme.css file should look like:
+
+
-## CSS class name structure
+// Responsive value
-All Canon components come with a set of CSS classes that you can use to style them. To make it
-easier to identify the class name you can use, we use a specific structure for the class names.
+`} />
-
-
-Every component has a unique prefix `.canon-` followed by the component name. Component props
-are represented using the `data-` attribute. That way, class names are easily identifiable.
-
-## Available CSS variables
-
-### Base colors
+## Base colors
These colors are used for special purposes like ring, scrollbar, ...
@@ -58,7 +119,7 @@ These colors are used for special purposes like ring, scrollbar, ...
- --canon-black
+ --bui-black
Pure black color. This one should be the same in light and dark themes.
@@ -66,7 +127,7 @@ These colors are used for special purposes like ring, scrollbar, ...
- --canon-white
+ --bui-white
Pure white color. This one should be the same in light and dark themes.
@@ -74,59 +135,59 @@ These colors are used for special purposes like ring, scrollbar, ...
- --canon-gray-1
+ --bui-gray-1You can use these mostly for backgrounds colors.
- --canon-gray-2
+ --bui-gray-2You can use these mostly for backgrounds colors.
- --canon-gray-3
+ --bui-gray-3You can use these mostly for backgrounds colors.
- --canon-gray-4
+ --bui-gray-4You can use these mostly for backgrounds colors.
- --canon-gray-5
+ --bui-gray-5You can use these mostly for backgrounds colors.
- --canon-gray-6
+ --bui-gray-6You can use these mostly for backgrounds colors.
- --canon-gray-7
+ --bui-gray-7You can use these mostly for backgrounds colors.
- --canon-gray-8
+ --bui-gray-8You can use these mostly for backgrounds colors.
-### Core background colors
+## Core background colors
These colors are used for the background of your application. We are mostly using for now a
-single elevated background for panels. `--canon-bg` should mostly use as the main background
+single elevated background for panels. `--bui-bg` should mostly use as the main background
color of your app.
@@ -139,94 +200,94 @@ color of your app.
- --canon-bg
+ --bui-bgThe background color of your Backstage instance.
- --canon-bg-surface-1
+ --bui-bg-surface-1Use for any panels or elevated surfaces.
- --canon-bg-surface-2
+ --bui-bg-surface-2Use for any panels or elevated surfaces.
- --canon-bg-solid
+ --bui-bg-solidUsed for solid background colors.
- --canon-bg-solid-hover
+ --bui-bg-solid-hoverUsed for solid background colors when hovered.
- --canon-bg-solid-pressed
+ --bui-bg-solid-pressedUsed for solid background colors when pressed.
- --canon-bg-solid-disabled
+ --bui-bg-solid-disabledUsed for solid background colors when disabled.
- --canon-bg-tint
+ --bui-bg-tintUsed for tint background colors.
- --canon-bg-tint-hover
+ --bui-bg-tint-hoverUsed for tint background colors when hovered.
- --canon-bg-tint-focus
+ --bui-bg-tint-focusUsed for tint background colors when active.
- --canon-bg-tint-disabled
+ --bui-bg-tint-disabledUsed for tint background colors when disabled.
- --canon-bg-danger
+ --bui-bg-dangerUsed to show errors information.
- --canon-bg-warning
+ --bui-bg-warningUsed to show warnings information.
- --canon-bg-success
+ --bui-bg-successUsed to show success information.
-### Foreground colors
+## Foreground colors
-Foreground colours are meant to work in pair with a background colours. Typeically this would work
+Foreground colours are meant to work in pair with a background colours. Typically this would work
for icons, texts, shapes, ... Use a matching name to know what foreground color to use. These colors
are prefixed with `fg` to make it easier to identify.
@@ -240,7 +301,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-primary
+ --bui-fg-primary
It should be used on top of main background surfaces.
@@ -248,7 +309,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-secondary
+ --bui-fg-secondary
It should be used on top of main background surfaces.
@@ -256,7 +317,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-link
+ --bui-fg-link
It should be used on top of main background surfaces.
@@ -264,7 +325,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-link-hover
+ --bui-fg-link-hover
It should be used on top of main background surfaces.
@@ -272,7 +333,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-disabled
+ --bui-fg-disabled
It should be used on top of main background surfaces.
@@ -280,7 +341,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-solid
+ --bui-fg-solid
It should be used on top of solid background colors.
@@ -288,7 +349,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-tint
+ --bui-fg-tint
It should be used on top of tint background colors.
@@ -296,7 +357,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-tint-disabled
+ --bui-fg-tint-disabled
It should be used on top of tint background colors when disabled.
@@ -304,7 +365,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-danger
+ --bui-fg-danger
It should be used on top of danger background colors.
@@ -312,7 +373,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-warning
+ --bui-fg-warning
It should be used on top of warning background colors.
@@ -320,7 +381,7 @@ are prefixed with `fg` to make it easier to identify.
- --canon-fg-success
+ --bui-fg-success
It should be used on top of success background colors.
@@ -329,7 +390,7 @@ are prefixed with `fg` to make it easier to identify.
-### Border colors
+## Border colors
These border colors are mostly meant to be used as borders on top of any components with
low contrast to help as a separator with the different background colors.
@@ -344,15 +405,13 @@ low contrast to help as a separator with the different background colors.
- --canon-border
-
-
- It should be used on top of `--canon-bg-surface-1`.
+ --bui-border
+ It should be used on top of `--bui-bg-surface-1`.
- --canon-border-hover
+ --bui-border-hover
Used when the component is interactive and hovered.
@@ -360,7 +419,7 @@ low contrast to help as a separator with the different background colors.
- --canon-border-pressed
+ --bui-border-pressed
Used when the component is interactive and focused.
@@ -368,32 +427,32 @@ low contrast to help as a separator with the different background colors.
- --canon-border-disabled
+ --bui-border-disabledUsed when the component is disabled.
- --canon-border-danger
+ --bui-border-danger
- It should be used on top of `--canon-bg-danger`.
+ It should be used on top of `--bui-bg-danger`.
- --canon-border-warning
+ --bui-border-warning
- It should be used on top of `--canon-bg-warning`.
+ It should be used on top of `--bui-bg-warning`.
- --canon-border-success
+ --bui-border-success
- It should be used on top of `--canon-bg-success`.
+ It should be used on top of `--bui-bg-success`.
-### Special colors
+## Special colors
These colors are used for special purposes like ring, scrollbar, ...
@@ -407,28 +466,28 @@ These colors are used for special purposes like ring, scrollbar, ...
- --canon-ring
+ --bui-ringThe color of the ring.
- --canon-scrollbar
+ --bui-scrollbarThe color of the scrollbar.
- --canon-scrollbar-thumb
+ --bui-scrollbar-thumbThe color of the scrollbar thumb.
-### Font families
+## Font families
-We have two fonts that we use across Canon. The first one is the sans-serif
+We have two fonts that we use across Backstage UI. The first one is the sans-serif
font that we use for the body of the application. The second one is the
monospace font that we use for code blocks and tables.
@@ -442,22 +501,22 @@ monospace font that we use for code blocks and tables.
- --canon-font-regular
+ --bui-font-regularThe sans-serif font for the theme.
- --canon-font-mono
+ --bui-font-monospaceThe monospace font for the theme.
-### Font weights
+## Font weights
-We have two font weights that we use across Canon. Regular or Bold.
+We have two font weights that we use across Backstage UI. Regular or Bold.
@@ -469,27 +528,27 @@ We have two font weights that we use across Canon. Regular or Bold.
- --canon-font-weight-regular
+ --bui-font-weight-regularThe regular font weight for the theme.
- --canon-font-weight-bold
+ --bui-font-weight-boldThe bold font weight for the theme.
-### Spacing
+## Spacing
-We built a spacing system based on a single value `--canon-space`. This value is
+We built a spacing system based on a single value `--bui-space`. This value is
used to calculate the spacing for all the components. By default if you would like to
increase or decrease the spacing between your components you can do it simply by updating
-`--canon-space` and it will apply to all spacing values.
+`--bui-space` and it will apply to all spacing values.
-`--canon-space` is not used directly in any components but serve as an easy way to
+`--bui-space` is not used directly in any components but serve as an easy way to
calculate the other values.
@@ -502,7 +561,7 @@ calculate the other values.
- --canon-space
+ --bui-space
The base unit for the spacing system. Default value is `0.25rem`.
@@ -524,104 +583,104 @@ tokens for pretty much each spacing properties like padding, margin, gaps, ...
- --canon-space-0_5
+ --bui-space-0_5
- Base unit (`--canon-space`) times 0.5.
+ Base unit (`--bui-space`) times 0.5.
- --canon-space-1
+ --bui-space-1
- Base unit (`--canon-space`).
+ Base unit (`--bui-space`).
- --canon-space-1_5
+ --bui-space-1_5
- Base unit (`--canon-space`) times 1.5.
+ Base unit (`--bui-space`) times 1.5.
- --canon-space-2
+ --bui-space-2
- Base unit (`--canon-space`) times 2.
+ Base unit (`--bui-space`) times 2.
- --canon-space-3
+ --bui-space-3
- Base unit (`--canon-space`) times 3.
+ Base unit (`--bui-space`) times 3.
- --canon-space-4
+ --bui-space-4
- Base unit (`--canon-space`) times 4.
+ Base unit (`--bui-space`) times 4.
- --canon-space-5
+ --bui-space-5
- Base unit (`--canon-space`) times 5.
+ Base unit (`--bui-space`) times 5.
- --canon-space-6
+ --bui-space-6
- Base unit (`--canon-space`) times 6.
+ Base unit (`--bui-space`) times 6.
- --canon-space-7
+ --bui-space-7
- Base unit (`--canon-space`) times 7.
+ Base unit (`--bui-space`) times 7.
- --canon-space-8
+ --bui-space-8
- Base unit (`--canon-space`) times 8.
+ Base unit (`--bui-space`) times 8.
- --canon-space-9
+ --bui-space-9
- Base unit (`--canon-space`) times 9.
+ Base unit (`--bui-space`) times 9.
- --canon-space-10
+ --bui-space-10
- Base unit (`--canon-space`) times 10.
+ Base unit (`--bui-space`) times 10.
- --canon-space-11
+ --bui-space-11
- Base unit (`--canon-space`) times 11.
+ Base unit (`--bui-space`) times 11.
- --canon-space-12
+ --bui-space-12
- Base unit (`--canon-space`) times 12.
+ Base unit (`--bui-space`) times 12.
- --canon-space-13
+ --bui-space-13
- Base unit (`--canon-space`) times 13.
+ Base unit (`--bui-space`) times 13.
- --canon-space-14
+ --bui-space-14
- Base unit (`--canon-space`) times 14.
+ Base unit (`--bui-space`) times 14.
-### Radius
+## Radius
We use a radius system to make sure that the components have a consistent look and feel.
@@ -635,7 +694,7 @@ We use a radius system to make sure that the components have a consistent look a
- --canon-radius-1
+ --bui-radius-1
The radius of the component. Default value is `0.125rem`.
@@ -643,7 +702,7 @@ We use a radius system to make sure that the components have a consistent look a
- --canon-radius-2
+ --bui-radius-2
The radius of the component. Default value is `0.25rem`.
@@ -651,7 +710,7 @@ We use a radius system to make sure that the components have a consistent look a
- --canon-radius-3
+ --bui-radius-3
The radius of the component. Default value is `0.5rem`.
@@ -659,7 +718,7 @@ We use a radius system to make sure that the components have a consistent look a
- --canon-radius-4
+ --bui-radius-4
The radius of the component. Default value is `0.75rem`.
@@ -667,7 +726,7 @@ We use a radius system to make sure that the components have a consistent look a
- --canon-radius-5
+ --bui-radius-5
The radius of the component. Default value is `1rem`.
@@ -675,7 +734,7 @@ We use a radius system to make sure that the components have a consistent look a
- --canon-radius-6
+ --bui-radius-6
The radius of the component. Default value is `1.25rem`.
@@ -683,7 +742,7 @@ We use a radius system to make sure that the components have a consistent look a
- --canon-radius-full
+ --bui-radius-full
The radius of the component. Default value is `9999px`.
diff --git a/canon-docs/src/components/Banner/Banner.tsx b/docs-ui/src/components/Banner/Banner.tsx
similarity index 62%
rename from canon-docs/src/components/Banner/Banner.tsx
rename to docs-ui/src/components/Banner/Banner.tsx
index 0f055d6c92..9ad2ff0ced 100644
--- a/canon-docs/src/components/Banner/Banner.tsx
+++ b/docs-ui/src/components/Banner/Banner.tsx
@@ -1,18 +1,3 @@
-/*
- * Copyright 2024 The Backstage Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
import styles from './styles.module.css';
export const Banner = ({
diff --git a/docs-ui/src/components/Banner/index.ts b/docs-ui/src/components/Banner/index.ts
new file mode 100644
index 0000000000..f4930c0719
--- /dev/null
+++ b/docs-ui/src/components/Banner/index.ts
@@ -0,0 +1 @@
+export { Banner } from './Banner';
diff --git a/canon-docs/src/components/Banner/styles.module.css b/docs-ui/src/components/Banner/styles.module.css
similarity index 100%
rename from canon-docs/src/components/Banner/styles.module.css
rename to docs-ui/src/components/Banner/styles.module.css
diff --git a/docs-ui/src/components/Changelog/index.tsx b/docs-ui/src/components/Changelog/index.tsx
new file mode 100644
index 0000000000..224e23d668
--- /dev/null
+++ b/docs-ui/src/components/Changelog/index.tsx
@@ -0,0 +1,75 @@
+import { changelog } from '@/utils/changelog';
+import { MDXRemote } from 'next-mdx-remote-client/rsc';
+import { formattedMDXComponents } from '@/mdx-components';
+
+export function Changelog() {
+ // Group changelog entries by version
+ const groupedChangelog = changelog.reduce((acc, entry) => {
+ if (!acc[entry.version]) {
+ acc[entry.version] = [];
+ }
+ acc[entry.version].push(entry);
+ return acc;
+ }, {} as Record);
+
+ // Sort versions in descending order
+ const sortedVersions = Object.keys(groupedChangelog).sort((a, b) =>
+ b.localeCompare(a),
+ );
+
+ const content = sortedVersions
+ .map(version => {
+ const entries = groupedChangelog[version];
+
+ // Group entries by bump type
+ const groupedByBump = entries.reduce((acc, entry) => {
+ const bumpType = entry.type || 'other';
+ if (!acc[bumpType]) {
+ acc[bumpType] = [];
+ }
+ acc[bumpType].push(entry);
+ return acc;
+ }, {} as Record);
+
+ // Define the order of bump types
+ const bumpOrder = ['breaking', 'new', 'fix', 'other'];
+
+ const bumpSections = bumpOrder
+ .filter(bumpType => groupedByBump[bumpType]?.length > 0)
+ .map(bumpType => {
+ const bumpEntries = groupedByBump[bumpType];
+ let sectionTitle = 'Other Changes';
+ if (bumpType === 'breaking') {
+ sectionTitle = 'Breaking Changes';
+ } else if (bumpType === 'new') {
+ sectionTitle = 'New Features';
+ } else if (bumpType === 'fix') {
+ sectionTitle = 'Bug Fixes';
+ }
+
+ return `### ${sectionTitle}
+
+ ${bumpEntries
+ .map(e => {
+ const prs =
+ e.prs.length > 0 &&
+ e.prs
+ .map(
+ pr =>
+ `[#${pr}](https://github.com/backstage/backstage/pull/${pr})`,
+ )
+ .join(', ');
+ return `- ${e.description} ${prs}`;
+ })
+ .join('\n')}`;
+ })
+ .join('\n\n');
+
+ return `## Version ${version}
+
+ ${bumpSections}`;
+ })
+ .join('\n');
+
+ return ;
+}
diff --git a/docs-ui/src/components/ChangelogComponent/index.tsx b/docs-ui/src/components/ChangelogComponent/index.tsx
new file mode 100644
index 0000000000..f8832b0731
--- /dev/null
+++ b/docs-ui/src/components/ChangelogComponent/index.tsx
@@ -0,0 +1,32 @@
+import { changelog } from '@/utils/changelog';
+import { MDXRemote } from 'next-mdx-remote-client/rsc';
+import { formattedMDXComponents } from '@/mdx-components';
+import type { Component } from '@/utils/changelog';
+
+export const ChangelogComponent = ({ component }: { component: Component }) => {
+ const componentChangelog = changelog.filter(c =>
+ c.components.includes(component),
+ );
+
+ return (
+ {
+ const prs =
+ change.prs.length > 0 &&
+ change.prs
+ .map(
+ pr =>
+ `[#${pr}](https://github.com/backstage/backstage/pull/${pr})`,
+ )
+ .join(', ');
+ return `- \`${change.version}\` - ${change.description} ${prs}`;
+ })
+ .join('\n')}`}
+ />
+ );
+};
diff --git a/docs-ui/src/components/Chip/Chip.tsx b/docs-ui/src/components/Chip/Chip.tsx
new file mode 100644
index 0000000000..b348b46c32
--- /dev/null
+++ b/docs-ui/src/components/Chip/Chip.tsx
@@ -0,0 +1,16 @@
+import { ReactNode } from 'react';
+import styles from './styles.module.css';
+
+export const Chip = ({
+ children,
+ head = false,
+}: {
+ children: ReactNode;
+ head?: boolean;
+}) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/docs-ui/src/components/Chip/index.ts b/docs-ui/src/components/Chip/index.ts
new file mode 100644
index 0000000000..63314f5e37
--- /dev/null
+++ b/docs-ui/src/components/Chip/index.ts
@@ -0,0 +1 @@
+export { Chip } from './Chip';
diff --git a/docs-ui/src/components/Chip/styles.module.css b/docs-ui/src/components/Chip/styles.module.css
new file mode 100644
index 0000000000..01ee29311d
--- /dev/null
+++ b/docs-ui/src/components/Chip/styles.module.css
@@ -0,0 +1,26 @@
+.chip {
+ display: inline-flex;
+ align-items: center;
+ font-family: monospace;
+ font-size: 13px;
+ border-radius: 6px;
+ padding: 0px 8px;
+ height: 24px;
+ margin-right: 4px;
+ background-color: #f0f0f0;
+ color: #5d5d5d;
+}
+
+.head {
+ background-color: #eaf2fd;
+ color: #2563eb;
+}
+
+[data-theme-mode='dark'] .chip {
+ background-color: #2c2c2c;
+ color: #fff;
+}
+
+[data-theme-mode='dark'] .chip.head {
+ background-color: #33405b;
+}
diff --git a/canon-docs/src/components/CodeBlock/client.tsx b/docs-ui/src/components/CodeBlock/client.tsx
similarity index 53%
rename from canon-docs/src/components/CodeBlock/client.tsx
rename to docs-ui/src/components/CodeBlock/client.tsx
index 27f5bc1a02..666d880358 100644
--- a/canon-docs/src/components/CodeBlock/client.tsx
+++ b/docs-ui/src/components/CodeBlock/client.tsx
@@ -1,8 +1,13 @@
'use client';
import { CodeBlockProps } from '.';
-import { Text } from '../../../../packages/canon';
import styles from './styles.module.css';
+import parse from 'html-react-parser';
+
+// Convert markdown-style backticks to HTML code tags
+function parseMarkdownInlineCode(text: string): string {
+ return text.replace(/`([^`]+)`/g, '$1');
+}
export const CodeBlockClient = ({
out,
@@ -15,10 +20,10 @@ export const CodeBlockClient = ({
diff --git a/docs-ui/src/components/LayoutComponents/index.ts b/docs-ui/src/components/LayoutComponents/index.ts
new file mode 100644
index 0000000000..8efb793bbd
--- /dev/null
+++ b/docs-ui/src/components/LayoutComponents/index.ts
@@ -0,0 +1 @@
+export { LayoutComponents } from './LayoutComponents';
diff --git a/canon-docs/src/components/LayoutComponents/svgs/box.tsx b/docs-ui/src/components/LayoutComponents/svgs/box.tsx
similarity index 56%
rename from canon-docs/src/components/LayoutComponents/svgs/box.tsx
rename to docs-ui/src/components/LayoutComponents/svgs/box.tsx
index b7f6a097e5..ce1a746f32 100644
--- a/canon-docs/src/components/LayoutComponents/svgs/box.tsx
+++ b/docs-ui/src/components/LayoutComponents/svgs/box.tsx
@@ -1,18 +1,3 @@
-/*
- * Copyright 2024 The Backstage Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
export const BoxSvg = () => {
return (