diff --git a/PLIPs.md b/PLIPs.md
new file mode 100644
index 0000000000..669bdccfb1
--- /dev/null
+++ b/PLIPs.md
@@ -0,0 +1,8 @@
+# Plone Improvement Proposals (PLIPs)
+
+For details of the PLIP process, read the following.
+
+- [Plone Improvement Proposals (PLIPs)](https://6.docs.plone.org/contributing/core/plips.html)
+- [PLIP review](https://6.docs.plone.org/contributing/core/plip-review.html)
+
+You can also [browse the current list of open PLIPs for Volto](https://github.com/plone/volto/labels/03%20type%3A%20feature%20(plip)).
diff --git a/packages/cmsui/.eslintrc.cjs b/packages/cmsui/.eslintrc.cjs
new file mode 100644
index 0000000000..b3a71fc44c
--- /dev/null
+++ b/packages/cmsui/.eslintrc.cjs
@@ -0,0 +1,11 @@
+/** @type {import('eslint').Linter.Config} */
+module.exports = {
+ overrides: [
+ {
+ files: ['**/*.ts', '**/*.tsx'],
+ extends: [
+ 'plugin:react/jsx-runtime', // We only want this for non-library code (eg. volto add-ons)
+ ],
+ },
+ ],
+};
diff --git a/packages/cmsui/.release-it.json b/packages/cmsui/.release-it.json
new file mode 100644
index 0000000000..507f953edc
--- /dev/null
+++ b/packages/cmsui/.release-it.json
@@ -0,0 +1,29 @@
+{
+ "plugins": {
+ "../scripts/prepublish.js": {}
+ },
+ "hooks": {
+ "after:bump": [
+ "pipx run towncrier build --draft --yes --version ${version} > .changelog.draft",
+ "pipx run towncrier build --yes --version ${version}"
+ ],
+ "after:release": "rm .changelog.draft"
+ },
+ "npm": {
+ "publish": false
+ },
+ "git": {
+ "commitArgs": ["--no-verify"],
+ "changelog": "pipx run towncrier build --draft --yes --version 0.0.0",
+ "requireUpstream": false,
+ "requireCleanWorkingDir": false,
+ "commitMessage": "Release @plone/cmsui ${version}",
+ "tagName": "plone-cmsui-${version}",
+ "tagAnnotation": "Release @plone/cmsui ${version}"
+ },
+ "github": {
+ "release": true,
+ "releaseName": "@plone/cmsui ${version}",
+ "releaseNotes": "cat .changelog.draft"
+ }
+}
diff --git a/packages/cmsui/.storybook/Logo.svg b/packages/cmsui/.storybook/Logo.svg
new file mode 100644
index 0000000000..5a7ba56902
--- /dev/null
+++ b/packages/cmsui/.storybook/Logo.svg
@@ -0,0 +1,21 @@
+
diff --git a/packages/cmsui/.storybook/main.ts b/packages/cmsui/.storybook/main.ts
new file mode 100644
index 0000000000..cf9f4ddec3
--- /dev/null
+++ b/packages/cmsui/.storybook/main.ts
@@ -0,0 +1,41 @@
+import type { StorybookConfig } from '@storybook/react-vite';
+import { mergeConfig } from 'vite';
+
+const config: StorybookConfig = {
+ // For some reason the property does not allow negation
+ // https://github.com/storybookjs/storybook/issues/11181#issuecomment-1535288804
+ stories: [
+ '../components/**/*.mdx',
+ '../components/**/*.stories.@(js|jsx|ts|tsx)',
+ ],
+ addons: [
+ '@storybook/addon-links',
+ '@storybook/addon-essentials',
+ '@storybook/addon-interactions',
+ ],
+ framework: {
+ name: '@storybook/react-vite',
+ options: {},
+ },
+ docs: {
+ autodocs: 'tag',
+ },
+ typescript: {
+ reactDocgen: 'react-docgen-typescript',
+ reactDocgenTypescriptOptions: {
+ compilerOptions: {
+ allowSyntheticDefaultImports: false,
+ esModuleInterop: false,
+ },
+ propFilter: () => true,
+ },
+ },
+ async viteFinal(config) {
+ return mergeConfig(config, {
+ build: {
+ minify: false,
+ },
+ });
+ },
+};
+export default config;
diff --git a/packages/cmsui/.storybook/manager.js b/packages/cmsui/.storybook/manager.js
new file mode 100644
index 0000000000..2e62084432
--- /dev/null
+++ b/packages/cmsui/.storybook/manager.js
@@ -0,0 +1,6 @@
+import { addons } from '@storybook/manager-api';
+import theme from './theme';
+
+addons.setConfig({
+ theme,
+});
diff --git a/packages/cmsui/.storybook/preview-head.html b/packages/cmsui/.storybook/preview-head.html
new file mode 100644
index 0000000000..05da1e9dfb
--- /dev/null
+++ b/packages/cmsui/.storybook/preview-head.html
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/packages/cmsui/.storybook/preview.ts b/packages/cmsui/.storybook/preview.ts
new file mode 100644
index 0000000000..362843c9bc
--- /dev/null
+++ b/packages/cmsui/.storybook/preview.ts
@@ -0,0 +1,24 @@
+import './storybook-base.css';
+import '@plone/components/dist/basic.css';
+import '../main.css';
+import config from '@plone/registry';
+import installSlots from '../config';
+import installBlocks from '@plone/blocks';
+
+config.set('slots', {});
+config.set('utilities', {});
+installSlots(config);
+installBlocks(config);
+
+export const parameters = {
+ backgrounds: {
+ default: 'light',
+ },
+ actions: { argTypesRegex: '^on[A-Z].*' },
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/,
+ },
+ },
+};
diff --git a/packages/cmsui/.storybook/storybook-base.css b/packages/cmsui/.storybook/storybook-base.css
new file mode 100644
index 0000000000..f55ba4bf27
--- /dev/null
+++ b/packages/cmsui/.storybook/storybook-base.css
@@ -0,0 +1,19 @@
+/* Base styles */
+:root {
+ --basic-font-family: system-ui;
+ --basic-font-size: 16px;
+ background: var(--background-color);
+ font-family: var(--basic-font-family);
+ font-size: var(--basic-font-size);
+ line-height: 1.5;
+}
+
+.sbdocs.sbdocs-content {
+ p {
+ font-size: 16px;
+ }
+}
+
+#storybook-root {
+ width: 100vw;
+}
diff --git a/packages/cmsui/.storybook/theme.ts b/packages/cmsui/.storybook/theme.ts
new file mode 100644
index 0000000000..3262e1f700
--- /dev/null
+++ b/packages/cmsui/.storybook/theme.ts
@@ -0,0 +1,10 @@
+import { create } from '@storybook/theming/create';
+import logo from './Logo.svg';
+
+export default create({
+ base: 'light',
+ brandTitle: '@plone/components StoryBook',
+ brandUrl: 'https://plone-components.netlify.app/',
+ brandImage: logo,
+ brandTarget: '_self',
+});
diff --git a/packages/cmsui/.stylelintrc b/packages/cmsui/.stylelintrc
new file mode 100644
index 0000000000..8ac62f8d0f
--- /dev/null
+++ b/packages/cmsui/.stylelintrc
@@ -0,0 +1,14 @@
+{
+ "extends": ["stylelint-config-idiomatic-order"],
+ "plugins": ["stylelint-prettier"],
+ "overrides": [
+ {
+ "files": ["**/*.scss"],
+ "customSyntax": "postcss-scss"
+ }
+ ],
+ "rules": {
+ "prettier/prettier": true,
+ "order/properties-alphabetical-order": null
+ }
+}
diff --git a/packages/cmsui/CHANGELOG.md b/packages/cmsui/CHANGELOG.md
new file mode 100644
index 0000000000..2969638257
--- /dev/null
+++ b/packages/cmsui/CHANGELOG.md
@@ -0,0 +1,11 @@
+# @plone/cmsui Release Notes
+
+
+
+
+
+## 1.0.0 (unreleased)
diff --git a/packages/cmsui/README.md b/packages/cmsui/README.md
new file mode 100644
index 0000000000..ee5d757cd6
--- /dev/null
+++ b/packages/cmsui/README.md
@@ -0,0 +1,8 @@
+# `@plone/cmsui`
+
+This package provides default structural slots for Plone 7 and the API-first story.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
diff --git a/packages/cmsui/index.ts b/packages/cmsui/index.ts
new file mode 100644
index 0000000000..7258290153
--- /dev/null
+++ b/packages/cmsui/index.ts
@@ -0,0 +1,5 @@
+import type { ConfigType } from '@plone/registry';
+
+export default function install(config: ConfigType) {
+ return config;
+}
diff --git a/packages/cmsui/main.css b/packages/cmsui/main.css
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/cmsui/news/.gitkeep b/packages/cmsui/news/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/cmsui/package.json b/packages/cmsui/package.json
new file mode 100644
index 0000000000..28597732df
--- /dev/null
+++ b/packages/cmsui/package.json
@@ -0,0 +1,79 @@
+{
+ "name": "@plone/cmsui",
+ "description": "Plone CMSUI components",
+ "maintainers": [
+ {
+ "name": "Plone Foundation",
+ "url": "https://plone.org"
+ }
+ ],
+ "funding": "https://github.com/sponsors/plone",
+ "license": "MIT",
+ "version": "1.0.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/plone/volto.git"
+ },
+ "bugs": {
+ "url": "https://github.com/plone/volto/issues"
+ },
+ "homepage": "https://plone.org",
+ "keywords": [
+ "volto",
+ "plone",
+ "plone6",
+ "react",
+ "helpers"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "main": "index.ts",
+ "scripts": {
+ "test": "vitest",
+ "dry-release": "release-it --dry-run",
+ "release": "release-it",
+ "release-major-alpha": "release-it major --preRelease=alpha",
+ "release-alpha": "release-it --preRelease=alpha",
+ "storybook": "storybook dev -p 6006",
+ "build-storybook": "storybook build"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ },
+ "dependencies": {
+ "@plone/client": "workspace:*",
+ "@plone/components": "workspace:*",
+ "@plone/registry": "workspace:*",
+ "react-aria-components": "^1.5.0"
+ },
+ "devDependencies": {
+ "@plone/types": "workspace:*",
+ "@storybook/addon-essentials": "^8.0.4",
+ "@storybook/addon-interactions": "^8.0.4",
+ "@storybook/addon-links": "^8.0.4",
+ "@storybook/addon-mdx-gfm": "^8.0.4",
+ "@storybook/blocks": "^8.0.4",
+ "@storybook/manager-api": "^8.0.4",
+ "@storybook/react": "^8.0.4",
+ "@storybook/react-vite": "^8.0.4",
+ "@storybook/theming": "^8.0.4",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "eslint-plugin-storybook": "^0.8.0",
+ "jest-axe": "^8.0.0",
+ "release-it": "17.1.1",
+ "storybook": "^8.0.4",
+ "tsconfig": "workspace:*",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.8",
+ "vitest": "^2.1.3",
+ "vitest-axe": "^0.1.0"
+ }
+}
diff --git a/packages/cmsui/setupTesting.ts b/packages/cmsui/setupTesting.ts
new file mode 100644
index 0000000000..8bc87fa36e
--- /dev/null
+++ b/packages/cmsui/setupTesting.ts
@@ -0,0 +1,3 @@
+import '@testing-library/jest-dom';
+import { toHaveNoViolations } from 'jest-axe';
+expect.extend(toHaveNoViolations);
diff --git a/packages/cmsui/stories.ts b/packages/cmsui/stories.ts
new file mode 100644
index 0000000000..6a668df163
--- /dev/null
+++ b/packages/cmsui/stories.ts
@@ -0,0 +1,22 @@
+export const storyData = {
+ blocks: {
+ '7ab29abe-b38c-406b-94d7-b270e544a998': {
+ '@type': 'slate',
+ value: [
+ {
+ type: 'p',
+ children: [
+ {
+ text: 'Lorem ipsum dolor sit amet eu tempus ornare elit. Curabitur egestas quisque molestie pellentesque nunc imperdiet posuere morbi nunc eleifend. Volutpat enim augue blandit aliquam interdum pulvinar eu mattis congue. Eleifend mauris ut fermentum egestas mi faucibus adipiscing arcu nibh scelerisque justo habitasse. Mi consectetur hac maecenas leo dictumst vitae phasellus quam praesent vivamus nullam imperdiet integer mauris.',
+ },
+ ],
+ },
+ ],
+ plaintext:
+ 'Lorem ipsum dolor sit amet eu tempus ornare elit. Curabitur egestas quisque molestie pellentesque nunc imperdiet posuere morbi nunc eleifend. Volutpat enim augue blandit aliquam interdum pulvinar eu mattis congue. Eleifend mauris ut fermentum egestas mi faucibus adipiscing arcu nibh scelerisque justo habitasse. Mi consectetur hac maecenas leo dictumst vitae phasellus quam praesent vivamus nullam imperdiet integer mauris.',
+ },
+ },
+ blocks_layout: {
+ items: ['7ab29abe-b38c-406b-94d7-b270e544a998'],
+ },
+};
diff --git a/packages/cmsui/towncrier.toml b/packages/cmsui/towncrier.toml
new file mode 100644
index 0000000000..3ef721f378
--- /dev/null
+++ b/packages/cmsui/towncrier.toml
@@ -0,0 +1,33 @@
+[tool.towncrier]
+filename = "CHANGELOG.md"
+directory = "news/"
+title_format = "## {version} ({project_date})"
+underlines = ["", "", ""]
+template = "../scripts/templates/towncrier_template.jinja"
+start_string = "\n"
+issue_format = "[#{issue}](https://github.com/plone/volto/issues/{issue})"
+
+[[tool.towncrier.type]]
+directory = "breaking"
+name = "Breaking"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "feature"
+name = "Feature"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "bugfix"
+name = "Bugfix"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "internal"
+name = "Internal"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "documentation"
+name = "Documentation"
+showcontent = true
diff --git a/packages/cmsui/tsconfig.json b/packages/cmsui/tsconfig.json
new file mode 100644
index 0000000000..2965402840
--- /dev/null
+++ b/packages/cmsui/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "tsconfig/react-library.json",
+ "include": ["**/*.ts", "**/*.tsx"],
+ "exclude": [
+ "node_modules",
+ "build",
+ "public",
+ "coverage",
+ "src/**/*.test.{js,jsx,ts,tsx}",
+ "src/**/*.spec.{js,jsx,ts,tsx}",
+ "src/**/*.stories.{js,jsx,ts,tsx}"
+ ]
+}
diff --git a/packages/cmsui/vitest.config.ts b/packages/cmsui/vitest.config.ts
new file mode 100644
index 0000000000..fddf5f61f7
--- /dev/null
+++ b/packages/cmsui/vitest.config.ts
@@ -0,0 +1,14 @@
+import { defineConfig } from 'vitest/config';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ setupFiles: './setupTesting.ts',
+ // you might want to disable it, if you don't have tests that rely on CSS
+ // since parsing CSS is slow
+ css: true,
+ exclude: ['**/node_modules/**', '**/lib/**'],
+ },
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ae6dfec91f..3f68d68045 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -517,6 +517,91 @@ importers:
specifier: ^7.2.0
version: 7.2.0(debug@4.3.4)
+ packages/cmsui:
+ dependencies:
+ '@plone/client':
+ specifier: workspace:*
+ version: link:../client
+ '@plone/components':
+ specifier: workspace:*
+ version: link:../components
+ '@plone/registry':
+ specifier: workspace:*
+ version: link:../registry
+ react:
+ specifier: ^16.8.0 || ^17.0.0 || ^18.0.0
+ version: 18.2.0
+ react-aria-components:
+ specifier: ^1.5.0
+ version: 1.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ react-dom:
+ specifier: ^16.8.0 || ^17.0.0 || ^18.0.0
+ version: 18.2.0(react@18.2.0)
+ devDependencies:
+ '@plone/types':
+ specifier: workspace:*
+ version: link:../types
+ '@storybook/addon-essentials':
+ specifier: ^8.0.4
+ version: 8.0.8(@types/react@18.3.12)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ '@storybook/addon-interactions':
+ specifier: ^8.0.4
+ version: 8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3))
+ '@storybook/addon-links':
+ specifier: ^8.0.4
+ version: 8.0.8(react@18.2.0)
+ '@storybook/addon-mdx-gfm':
+ specifier: ^8.0.4
+ version: 8.0.8
+ '@storybook/blocks':
+ specifier: ^8.0.4
+ version: 8.0.8(@types/react@18.3.12)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ '@storybook/manager-api':
+ specifier: ^8.0.4
+ version: 8.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ '@storybook/react':
+ specifier: ^8.0.4
+ version: 8.0.8(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.6.3)
+ '@storybook/react-vite':
+ specifier: ^8.0.4
+ version: 8.0.8(@preact/preset-vite@2.8.2(@babel/core@7.25.8)(vite@5.4.9(@types/node@20.12.7)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3)))(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.9(@types/node@20.12.7)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3))
+ '@storybook/theming':
+ specifier: ^8.0.4
+ version: 8.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ '@types/react':
+ specifier: ^18
+ version: 18.3.12
+ '@types/react-dom':
+ specifier: ^18
+ version: 18.3.1
+ eslint-plugin-storybook:
+ specifier: ^0.8.0
+ version: 0.8.0(eslint@8.57.0)(typescript@5.6.3)
+ jest-axe:
+ specifier: ^8.0.0
+ version: 8.0.0
+ release-it:
+ specifier: 17.1.1
+ version: 17.1.1(typescript@5.6.3)
+ storybook:
+ specifier: ^8.0.4
+ version: 8.0.8(@babel/preset-env@7.24.4(@babel/core@7.25.8))(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ tsconfig:
+ specifier: workspace:*
+ version: link:../tsconfig
+ typescript:
+ specifier: ^5.6.3
+ version: 5.6.3
+ vite:
+ specifier: ^5.4.8
+ version: 5.4.9(@types/node@20.12.7)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3)
+ vitest:
+ specifier: ^2.1.3
+ version: 2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3)
+ vitest-axe:
+ specifier: ^0.1.0
+ version: 0.1.0(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.28.1)(sass@1.75.0)(terser@5.30.3))
+
packages/components:
dependencies:
'@react-aria/utils':
@@ -18378,9 +18463,11 @@ snapshots:
'@babel/plugin-transform-async-to-generator@7.24.1(@babel/core@7.25.8)':
dependencies:
'@babel/core': 7.25.8
- '@babel/helper-module-imports': 7.24.3
+ '@babel/helper-module-imports': 7.25.7
'@babel/helper-plugin-utils': 7.25.7
'@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.25.8)
+ transitivePeerDependencies:
+ - supports-color
'@babel/plugin-transform-block-scoped-functions@7.24.1(@babel/core@7.24.4)':
dependencies:
@@ -19143,11 +19230,11 @@ snapshots:
'@babel/preset-env@7.24.4(@babel/core@7.25.8)':
dependencies:
- '@babel/compat-data': 7.24.4
+ '@babel/compat-data': 7.25.8
'@babel/core': 7.25.8
- '@babel/helper-compilation-targets': 7.23.6
- '@babel/helper-plugin-utils': 7.24.0
- '@babel/helper-validator-option': 7.23.5
+ '@babel/helper-compilation-targets': 7.25.7
+ '@babel/helper-plugin-utils': 7.25.7
+ '@babel/helper-validator-option': 7.25.7
'@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.4(@babel/core@7.25.8)
'@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.1(@babel/core@7.25.8)
'@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.1(@babel/core@7.25.8)