Compare commits

...
Author SHA1 Message Date
ehconitin 9e6d384e9b add upgrade command 2026-06-05 21:28:38 +05:30
ehconitin 62dce10699 stash 2026-06-05 19:48:06 +05:30
ehconitin a81dd55f9d Merge remote-tracking branch 'upstream/main' into call-recording-api 2026-06-05 18:40:32 +05:30
nitinandGitHub e485b679ea [Call Recording] Add standard object (#21158)
Adds **Call Recording** as a first-class standard object (Twenty's
flat-metadata
standard-object system), with a hidden junction to calendar events and a
backfill
command for existing workspaces. Everything is gated behind the
`IS_CALL_RECORDING_ENABLED` feature flag.

### What's included
- **`CallRecording`**: audio/video files, transcript, status, recording
policy,
timing, external bot/recording ids. Label identifier is
`meetingOccurrenceKey`.
- **`CallRecordingCalendarEventAssociation`**: hidden junction linking a
recording
to a calendar event (dedupes one bot to many subscribers of the same
meeting).
- Full metadata graph via the flat-metadata builders: fields, indexes,
views,
  view fields/groups, record page layout, and navigation items.
- **Metadata-only reverse relation** on `CalendarEvent`: present in
standard
metadata, omitted from the TS entity class to avoid expanding recursive
  nested-insert types.
- **Upgrade command (2.9.0)** backfilling active/suspended workspaces:
  - Creates the full graph; idempotent (skips when it already exists).
- Moves a colliding custom `callRecording` object aside to
`callRecordingOld`
    (numeric suffix if that name is also taken).
- Navigation items (commands) are flag-gated by `universalIdentifier`,
so a custom object
    reusing the name is never gated.

### QA
Run locally against existing workspaces (with and without a name
collision) and a
freshly created workspace:
- [x] Backfill, collision: custom `callRecording` renamed to
`callRecordingOld`;
  standard graph created.
- [x] Backfill, no collision: standard graph created; unrelated custom
object untouched.
- [x] Idempotent: re-run is a no-op, with no duplicate metadata and
counts unchanged.
- [x] New workspace via `init()` produces an identical graph to the
backfill
  (`universalIdentifier` set-diff = 0).
- [x] Label identifier (`meetingOccurrenceKey`) holds position 0 in
non-widget views.
- [x] Nav items gated behind the feature flag; collision-renamed
object's nav
  expression re-pointed to its new name.
- [x] Unit tests cover collision name resolution and nav-gating logic.
2026-06-05 13:02:50 +00:00
ehconitin 22f9c3517b Merge remote-tracking branch 'upstream/call-recording-standard-object' into ehco/intelligent-benz-a7fa3b 2026-06-05 18:25:28 +05:30
ehconitin c629ea858b feat(server): add calendar event recording decision layer 2026-06-05 18:25:11 +05:30
Paul RastoinandGitHub a78b319877 sdk/v* twenty/v* namespaces (#21247)
# Introduction
Removed never used release dispatch workflow
Now assuming that anyone releasing will create both twenty and npm
family tags
Will create a workflow to ease this later

We will now start to have several github releases, one per namespace
2026-06-05 12:40:50 +00:00
ehconitin 05ea7ac120 Scope each call recording collision rename to its entity's application 2026-06-05 18:00:46 +05:30
ehconitin 0c1ca75981 Revert "Use from-to migration for call recording sync"
This reverts commit 312b6969aa.
2026-06-05 17:27:17 +05:30
ehconitin 1f63ff0f3d Revert "fix"
This reverts commit cc93aaa6b4.
2026-06-05 17:27:17 +05:30
Félix MalfaitandGitHub c3dd6b25a6 fix: use canonical oxlint rule id in lint-disable directives (#21253)
## What

Many `oxlint-disable` / `eslint-disable` directives across the repo
carry a corrupted rule id — `@typescripttypescript/<rule>` — most likely
a find-and-replace accident that mangled the eslint-era
`@typescript-eslint/` prefix.

oxlint matches disable directives **loosely by rule name**, so these
still suppress in practice (not a silent no-op), but the id is malformed
and misleading.

## Change

Replace them with the **canonical oxlint id** `typescript/<rule>` —
matching the plugin name and rule keys declared in `.oxlintrc.json` —
**127 files, 262 directives**:

| rule | count |
| --- | ----- |
| `typescript/no-explicit-any` | 250 |
| `typescript/ban-ts-comment` | 6 |
| `typescript/no-misused-promises` | 4 |
| `typescript/no-empty-object-type` | 2 |

- `twenty-server`: 122 files
- `twenty-front`: 5 files

Comment-only — no code or runtime changes.

## Verification

`oxlint --type-aware -c .oxlintrc.json` reports **0 warnings / 0
errors** for both `twenty-server` and `twenty-front`. Every changed line
is exactly the id correction inside a disable directive (262 insertions
/ 262 deletions, no collateral edits).

> Addresses the cubic review, which flagged that the canonical oxlint id
is `typescript/...` (no `@`). Worth noting the original
`@typescripttypescript/` was not actually a silent no-op — oxlint
matches these directives loosely by rule name — but `typescript/` is the
correct, config-aligned id.
2026-06-05 13:52:32 +02:00
ehconitin cc93aaa6b4 fix 2026-06-05 16:56:37 +05:30
ehconitin 312b6969aa Use from-to migration for call recording sync 2026-06-05 16:36:08 +05:30
6f9b59b224 Scaffold twenty-new-ui (#21236)
Scaffolds `twenty-new-ui`, the next-gen replacement for `twenty-ui`, on
**SCSS** Modules + **Base UI** (no Linaria).

- **Tooling**: Vite lib build, subpaths mirror twenty-ui, typed SCSS
Modules, Storybook + axe a11y, size-limit, Nx targets.
- **Theme**: single token source → nx generateTheme emits the CSS vars +
accessor; parity test asserts token-for-token match with twenty-ui.

Migrated a first `Toggle` component with its stories to allow
@charlesBochet to wire the new pixel-diff system.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 10:42:57 +00:00
ehconitin dd46d74d41 throw dont skip 2026-06-05 14:44:52 +05:30
Abdullah.andGitHub b60a91a075 fix(website): render releases from local notes, drop GitHub gate (#21238)
The releases page gated visible notes on fetchLatestGithubReleaseTag();
when that unauthenticated GitHub call was rate-limited at build (common
on shared Cloudflare build IPs, no GITHUB_TOKEN), it returned null and
getVisibleReleaseNotes returned [], rendering 'No releases are visible
yet for the current published version' — the exact prod symptom.
force-static made each deploy a coin flip.

Committed MDX (+images) is the single source of truth, so gate
visibility on nothing: render all local notes deterministically with no
network call. Removes the dev/prod divergence and deletes the now-dead
fetch-latest-release-tag.ts and get-visible-releases.ts.
2026-06-05 08:59:40 +00:00
ehconitin 1118c40864 fix 2026-06-05 14:28:40 +05:30
Charles BochetandGitHub cd540098f1 fix: pass reference_commit to Argos to resolve orphan PR builds (#21245)
## Summary

- Fixes Argos CI builds showing as "Orphan" (no reference branch) for PR
builds
- Computes the merge-base SHA between the PR head and `main` using the
GitHub API (`compareCommitsWithBasehead`) in the dispatch workflow
- Passes `reference_commit` in the `ci-privileged` dispatch payload so
it can be forwarded to the Argos upload API

## Context

PR builds on Argos were showing as "Orphan" because `ci-privileged`
(where the actual Argos upload happens) has no git history of the
`twenty` repo — it cannot compute the merge-base locally. Without a
`referenceCommit`, Argos can't determine which `main` build to compare
against.

The local `visual-diff.sh` script already passes
`ARGOS_REFERENCE_COMMIT` via `git merge-base HEAD main`, but the CI
pipeline was missing this. This PR adds equivalent logic using the
GitHub API (no checkout needed).

## Note for ci-privileged

The `upload-to-argos.ts` script in `ci-privileged` needs a corresponding
update to read `reference_commit` from the dispatch payload and pass it
as `referenceCommit` in the Argos API call:

```typescript
referenceCommit: process.env.REFERENCE_COMMIT || undefined,
```

## Test plan

- [ ] Verify the workflow runs successfully on a PR (merge-base step
computes a SHA)
- [ ] Confirm Argos PR builds are no longer marked as "Orphan" after the
ci-privileged counterpart is updated
2026-06-05 10:51:16 +02:00
Félix MalfaitandGitHub 1b30983307 fix(settings): gate the AI settings page on AI_SETTINGS, not the chat flag (#21239)
## Summary

Closes #21229.

The two AI role permissions behaved **opposite to their labels**. The
trap is that the flag's code name is the inverse of its UI label:

| `PermissionFlagType` | UI label | Section | Means |
|---|---|---|---|
| `AI` | **"Ask AI"** | Actions | End-user: chat with AI |
| `AI_SETTINGS` | **"AI"** | Member / settings | Admin: configure AI
agents |

Before this PR (on `main`):
- `AI` ("Ask AI", chat) gated **both** the AI chat **and** the AI
settings page.
- `AI_SETTINGS` ("AI", configure agents) gated **nothing** the user
could see.

So a chat-only user could reach the whole AI **configuration** page, and
toggling the "AI" settings permission did nothing — exactly the
misalignment reported in #21229.

## Root cause

`PermissionFlagType.AI` *reads* like "the AI permission", so it looks
like the natural gate for the AI settings page — but it's actually the
**chat** flag. The settings page (nav item + route) had been pointed at
`AI` in #21072 to match the Overview stats query
(`findWorkspaceAiStats`), which was itself mis-gated on `AI`. Both the
stats query and the rest of the settings surface are admin/config
features, so they belong on `AI_SETTINGS`.

## Changes

All three move the **AI settings surface** from the chat flag (`AI`) to
the settings flag (`AI_SETTINGS`); chat keeps following `AI`:

- `useSettingsNavigationItems.tsx` — AI nav item → `AI_SETTINGS`
- `SettingsRoutes.tsx` — AI settings route group → `AI_SETTINGS`
- `ai-workspace-stats.resolver.ts` — `findWorkspaceAiStats`
(settings-only, drives the Overview tab) → `AI_SETTINGS`

After this: the "AI" permission controls the AI settings page + its
Overview; the "Ask AI" permission controls the chat. Both toggles now
match their labels.

## Test plan

- [ ] Role with **only "Ask AI"** (`AI`): AI chat tabs/pane visible;
**Settings → AI is hidden** and the route is not reachable.
- [ ] Role with **only "AI"** (`AI_SETTINGS`): Settings → AI is visible,
Overview stats load; chat nav is hidden.
- [ ] Admin (both flags): everything works as before.

## Known follow-ups (out of scope — pre-existing, shared endpoints)

These remain on `AI` because they're shared with non-settings surfaces
and need either OR-gating or a resolver split, so a role with
`AI_SETTINGS` but **not** `AI` still can't use them yet:

- `getAiSystemPromptPreview` (Models/Prompts tabs) lives in the chat
resolver, class-gated `AI`; NestJS guards are additive so it can't be
cleanly method-overridden — it should be pulled into a settings
resolver.
- Agent reads `findManyAgents` / `findOneAgent` (agent create/edit
forms) are class-gated `AI` and shared with the **Workflow** editor and
**Roles** pages; these want a guard that accepts `AI ∨ AI_SETTINGS ∨
WORKFLOWS`.
2026-06-05 08:18:20 +00:00
ehconitin 817cf18075 Merge remote-tracking branch 'upstream/main' into call-recording-standard-object 2026-06-05 13:42:15 +05:30
martmullandGitHub 128d2d394d feat: allow apps to add view fields to existing views (defineViewField) (#21160)
## Summary

Lets a Twenty application add **view fields (columns) to an existing
view it does not own** — including standard views like the People index
view — without redeclaring/owning that view. This mirrors the existing,
working pattern by which an app adds a custom field to a standard object
via `defineField` + `objectUniversalIdentifier`.

The asymmetry being removed was purely in the manifest schema:
`ViewFieldManifest` only existed *nested* inside
`ViewManifest.fields[]`, so adding a view field forced declaring a
`ViewManifest` — which the sync treats as a view the app creates and
owns, and rejects when the UID is a standard view's. Validation,
persistence, the FK aggregator machinery, and uninstall cleanup were
already generic and cross-app-safe, so no engine changes were needed.

### Changes
- **twenty-shared:** new top-level `StandaloneViewFieldManifest`
(`ViewFieldManifest & { viewUniversalIdentifier }`),
`Manifest.viewFields`, and a `SyncableEntity.ViewField` member.
- **twenty-sdk:** `defineViewField` (validates `universalIdentifier` +
`viewUniversalIdentifier` + `fieldMetadataUniversalIdentifier`), CLI
manifest assembly of a top-level `viewFields` list, and `dev:add
viewField` scaffolding.
- **twenty-server:** one top-level loop over `manifest.viewFields` that
reuses the existing `fromViewFieldManifestToUniversalFlatViewField`
converter (already parameterized by `viewUniversalIdentifier`). No
validator/persistence/aggregator changes.

### Notes for maintainers
- Confirm the `Manifest.viewFields` optionality convention — implemented
as a **required** array to mirror `fields`/`views`.
- Two different apps adding a column for the same field to the same view
conflicts on the existing unique `(fieldMetadataId, viewId)` partial
index; the existing `flat-view-field-validator` duplicate check surfaces
this as a structured validation error.
- `dev:add viewField` scaffolding is included (was optional in the
plan).

## Test Plan
- [x] `twenty-shared` typecheck
- [x] `twenty-sdk` 364 unit tests + `buildManifest` assembly test
(rich-app fixture) + typecheck + prettier
- [x] `twenty-server` typecheck + `lint:diff-with-main`
- [x] **Server integration suite**
`successful-manifest-update-view-field.integration-spec.ts` (4/4):
- standalone view field attaches to the standard `allPeople` view
without recreating it (sync succeeds, no
`INVALID_VIEW_DATA`/`ENTITY_ALREADY_EXISTS`)
- uninstall removes the contributed column while the standard view + its
columns remain intact
  - duplicate `(view, field)` rejected with `METADATA_VALIDATION_FAILED`
  - unknown target view rejected
- [x] Sibling `successful-manifest-update-field.integration-spec.ts`
still green (no harness regression)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-05 08:06:30 +00:00
ff9b5a5cad chore: sync AI model catalog from models.dev (#21242)
Automated daily sync of `ai-providers.json` from
[models.dev](https://models.dev).

This PR updates pricing, context windows, and model availability based
on the latest data.
New models meeting inclusion criteria (tool calling, pricing data,
context limits) are added automatically.
Deprecated models are detected based on cost-efficiency within the same
model family.

**Please review before merging** — verify no critical models were
incorrectly deprecated.

Co-authored-by: FelixMalfait <6399865+FelixMalfait@users.noreply.github.com>
2026-06-05 09:22:38 +02:00
979047d004 fix: allow email change verification on self-hosted instances (#20123)
fixes #20117 

## Technical Details
Flow after fix:
1. User submits email change request
2. user.service.ts:517-524 calls sendVerificationEmail() with
verificationTrigger: EMAIL_UPDATE
3. Guard checks: verificationTrigger === SIGN_UP → false → guard skipped
4. Verification token generated, email rendered and sent via
emailService.send()
5. User receives confirmation email at new address
6. User clicks confirmation link → email update completes
---
Impact
- Minimal change: Only 3 lines modified in a single file
- No breaking changes: Sign-up verification behavior unchanged
- Security preserved: Email changes always require verification (correct
security behavior)
- Self-hosted friendly: Instance admins can disable sign-up verification
while keeping email change verification active

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-04 18:56:15 +00:00
ehconitin 544ebd487a Merge remote-tracking branch 'upstream/main' into call-recording-standard-object 2026-06-04 23:04:13 +05:30
ehconitin 5672a090f8 fix(call-recording): resequence upgrade command timestamp 2026-06-04 19:30:34 +05:30
ehconitin 8a8310dee3 Fix CallRecording standard object upgrade collisions 2026-06-04 19:12:28 +05:30
ehconitin 97c6b3a4b8 fix(call-recording): move upgrade command to 2-10 after main bumped to 2.10.0 2026-06-04 18:59:22 +05:30
ehconitin 8ce69d52ab refactor(call-recording): operation-based 2.9 upgrade sync with tested utils 2026-06-04 18:30:29 +05:30
ehconitin 9f76d44c22 fix(call-recording): use IconVideo for the call recording standard object 2026-06-04 18:30:29 +05:30
ehconitin e36d6290ea feat(call-recording): use snapshotted title as label identifier 2026-06-04 17:01:50 +05:30
ehconitin 33f3b4fbf9 fix(call-recording): inline immutable replace after upstream removed flat-entity util 2026-06-04 16:43:57 +05:30
ehconitin d8d02cb388 Merge remote-tracking branch 'upstream/main' into call-recording-standard-object 2026-06-04 16:24:20 +05:30
ehconitin 73e619174f fix(call-recording): rename all colliding custom objects, not just the first 2026-06-04 15:10:37 +05:30
ehconitin 426f87dfe6 update field name sourceApplicationId to applicationId 2026-06-04 14:37:39 +05:30
ehconitin 807ef86907 Merge remote-tracking branch 'upstream/main' into call-recording-standard-object
# Conflicts:
#	packages/twenty-server/src/database/commands/upgrade-version-command/2-9/2-9-upgrade-version-command.module.ts
2026-06-04 14:29:20 +05:30
ehconitin 7e7ff345da fix(call-recording): simplify failure state and calendar retention 2026-06-04 13:41:05 +05:30
ehconitin 69219d8871 fix(call-recording): resolve upgrade command timestamp collision with upstream 2026-06-04 12:22:04 +05:30
ehconitin 7a60706437 Merge remote-tracking branch 'upstream/main' into call-recording-standard-object
# Conflicts:
#	packages/twenty-client-sdk/src/metadata/generated/schema.graphql
#	packages/twenty-client-sdk/src/metadata/generated/schema.ts
#	packages/twenty-server/src/database/commands/upgrade-version-command/2-9/2-9-upgrade-version-command.module.ts
#	packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts
#	packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap
#	packages/twenty-shared/src/types/FeatureFlagKey.ts
2026-06-04 12:02:17 +05:30
ehconitin 62473fc5ea refactor(call-recording): collapse junction into direct CalendarEvent relation 2026-06-04 02:19:32 +05:30
ehconitin ff7819a353 fix 2026-06-03 20:48:07 +05:30
ehconitin 94badfd1c6 collision navigation command menu 2026-06-03 20:26:40 +05:30
ehconitin 9a8e3e8bc6 gate navigation commands with featrue flag 2026-06-03 19:41:26 +05:30
ehconitin 0a510a68de Merge remote-tracking branch 'upstream/main' into call-recording-standard-object 2026-06-03 18:36:53 +05:30
ehconitin bfe1046af4 feat(call-recording): sync full metadata graph in upgrade command, refine views, extract collision helpers 2026-06-03 18:36:02 +05:30
ehconitin be9b7a5222 Merge remote-tracking branch 'upstream/main' into call-recording-standard-object 2026-06-03 15:04:28 +05:30
ehconitin f770acbdee restore call-recording-calendar-event-association index view 2026-06-03 01:26:41 +05:30
ehconitin c68ff85f49 strip out unecessary code 2026-06-03 00:47:37 +05:30
ehconitin 7cc89c0811 regen 2026-06-02 23:01:42 +05:30
ehconitin 81da1bc2ac conflicts 2026-06-02 22:52:04 +05:30
ehconitin c2d3edb0f6 Merge remote-tracking branch 'upstream/main' into call-recording-standard-object 2026-06-02 22:51:55 +05:30
ehconitin c821869f9e chore: regenerate graphql and sdk metadata client for IS_CALL_RECORDING_ENABLED 2026-06-02 22:28:17 +05:30
ehconitin c36c1c7675 feat: add CallRecording standard object + 2.9 workspace sync command with custom-object collision rename 2026-06-02 21:59:45 +05:30
341 changed files with 15838 additions and 1475 deletions
+28 -3
View File
@@ -6,18 +6,43 @@ permissions:
on:
push:
tags:
- 'v*'
- 'twenty/v*'
- 'sdk/v*'
defaults:
run:
shell: bash --noprofile --norc -euo pipefail {0}
jobs:
deploy-tag:
dispatch-tag:
timeout-minutes: 3
runs-on: ubuntu-latest
steps:
- name: Resolve dispatch event from tag family
id: target
env:
REF_NAME: ${{ github.ref_name }}
run: |
case "$REF_NAME" in
twenty/v*)
event_type=auto-deploy-twenty
;;
sdk/v*)
event_type=auto-publish-npm
;;
*)
echo "Unsupported tag '$REF_NAME', expected 'twenty/v*' or 'sdk/v*'." >&2
exit 1
;;
esac
printf 'event_type=%s\n' "$event_type" >> "$GITHUB_OUTPUT"
- name: Repository Dispatch
env:
GH_TOKEN: ${{ secrets.TWENTY_INFRA_TOKEN }}
EVENT_TYPE: ${{ steps.target.outputs.event_type }}
REF_NAME: ${{ github.ref_name }}
run: |
gh api repos/twentyhq/twenty-infra/dispatches \
-f event_type=auto-deploy-tag \
-f "event_type=$EVENT_TYPE" \
-f "client_payload[github][ref_name]=$REF_NAME"
-63
View File
@@ -1,63 +0,0 @@
name: "Release: on merge"
permissions:
contents: write
on:
pull_request:
types:
- closed
defaults:
run:
shell: bash --noprofile --norc -euo pipefail {0}
jobs:
tag_and_release:
timeout-minutes: 10
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release')
steps:
- name: Check PR Author
id: check_author
env:
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
set -euo pipefail
if [[ "$PR_AUTHOR" != "github-actions[bot]" ]]; then
echo "PR author ($PR_AUTHOR) is not trusted. Exiting."
exit 1
fi
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
ref: main
- name: Get version from PR title
id: extract_version
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
set -euo pipefail
VERSION=$(printf '%s' "$PR_TITLE" | sed -n 's/.*Release v\([0-9][0-9.]*\).*/\1/p')
if [ -z "$VERSION" ]; then
echo "No valid version found in PR title. Exiting."
exit 1
fi
printf 'VERSION=%s\n' "$VERSION" >> "$GITHUB_ENV"
- name: Push new tag
run: |
set -euo pipefail
git config --global user.name 'Github Action Deploy'
git config --global user.email 'github-action-deploy@twenty.com'
git tag "v${{ env.VERSION }}"
git push origin "v${{ env.VERSION }}"
- uses: release-drafter/release-drafter@09c613e259eb8d4e7c81c2cb00618eb5fc4575a7 # v5
if: contains(github.event.pull_request.labels.*.name, 'create_release')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag: v${{ env.VERSION }}
@@ -11,6 +11,7 @@ on:
permissions:
actions: read
contents: read
pull-requests: read
jobs:
@@ -83,6 +84,33 @@ jobs:
core.setOutput('has_pr', 'true');
core.info(`PR #${prNumber}`);
- name: Compute merge-base for Argos reference
if: steps.check-artifact.outputs.exists == 'true' && steps.pr-info.outputs.has_pr == 'true'
id: merge-base
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
with:
script: |
const headSha = context.payload.workflow_run.head_sha;
try {
const { data: comparison } = await github.rest.repos.compareCommitsWithBasehead({
owner: context.repo.owner,
repo: context.repo.repo,
basehead: `main...${headSha}`,
});
if (comparison.merge_base_commit?.sha) {
core.setOutput('sha', comparison.merge_base_commit.sha);
core.info(`Merge base: ${comparison.merge_base_commit.sha}`);
} else {
core.info('Could not determine merge base — will skip reference_commit');
core.setOutput('sha', '');
}
} catch (error) {
core.warning(`Failed to compute merge base: ${error instanceof Error ? error.message : String(error)}`);
core.setOutput('sha', '');
}
- name: Dispatch to ci-privileged
if: steps.check-artifact.outputs.exists == 'true' && steps.pr-info.outputs.has_pr == 'true'
env:
@@ -92,15 +120,23 @@ jobs:
REPOSITORY: ${{ github.repository }}
BRANCH: ${{ github.event.workflow_run.head_branch }}
COMMIT: ${{ github.event.workflow_run.head_sha }}
REFERENCE_COMMIT: ${{ steps.merge-base.outputs.sha }}
run: |
gh api repos/twentyhq/ci-privileged/dispatches \
--method POST \
-f event_type=visual-regression \
-f "client_payload[pr_number]=$PR_NUMBER" \
-f "client_payload[run_id]=$WORKFLOW_RUN_ID" \
-f "client_payload[repo]=$REPOSITORY" \
-f "client_payload[branch]=$BRANCH" \
ARGS=(
--method POST
-f event_type=visual-regression
-f "client_payload[pr_number]=$PR_NUMBER"
-f "client_payload[run_id]=$WORKFLOW_RUN_ID"
-f "client_payload[repo]=$REPOSITORY"
-f "client_payload[branch]=$BRANCH"
-f "client_payload[commit]=$COMMIT"
)
if [ -n "$REFERENCE_COMMIT" ]; then
ARGS+=(-f "client_payload[reference_commit]=$REFERENCE_COMMIT")
fi
gh api repos/twentyhq/ci-privileged/dispatches "${ARGS[@]}"
dispatch-main:
if: >-
+1
View File
@@ -52,6 +52,7 @@
"packages/twenty-server",
"packages/twenty-emails",
"packages/twenty-ui",
"packages/twenty-new-ui",
"packages/twenty-utils",
"packages/twenty-zapier",
"packages/twenty-website",
@@ -3,7 +3,7 @@ import { NavigationMenuItemType } from 'twenty-shared/types';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
export default defineNavigationMenuItem({
universalIdentifier: 'c1a2b3c4-0001-4a7b-8c9d-0e1f2a3b4c5d',
universalIdentifier: 'e8031eca-d6ea-4a4b-b828-38227dba896a',
position: 0,
type: NavigationMenuItemType.OBJECT,
targetObjectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
@@ -0,0 +1,10 @@
import { defineViewField } from 'twenty-sdk/define';
import { ALL_POST_CARDS_VIEW_ID } from '../views/all-post-cards.view';
export default defineViewField({
fieldMetadataUniversalIdentifier: '7b57bd63-5a4c-46ca-9d52-42c8f02d1df6',
position: 5,
universalIdentifier: 'cd582d11-ea21-4dc3-b9c1-0298ce3b6b54',
viewUniversalIdentifier: ALL_POST_CARDS_VIEW_ID,
isVisible: true,
});
@@ -1787,6 +1787,7 @@ enum FeatureFlagKey {
IS_REST_METADATA_API_NEW_FORMAT_DIRECT
IS_LOGIC_FUNCTION_PREBUILT_MODE_ENABLED
IS_SETTINGS_DISCOVERY_HERO_ENABLED
IS_CALL_RECORDING_ENABLED
IS_WORKFLOW_RUN_STEP_LOGS_ENABLED
}
@@ -1414,7 +1414,7 @@ export interface FeatureFlag {
__typename: 'FeatureFlag'
}
export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAIL_GROUP_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_REST_METADATA_API_NEW_FORMAT_DIRECT' | 'IS_LOGIC_FUNCTION_PREBUILT_MODE_ENABLED' | 'IS_SETTINGS_DISCOVERY_HERO_ENABLED' | 'IS_WORKFLOW_RUN_STEP_LOGS_ENABLED'
export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAIL_GROUP_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_REST_METADATA_API_NEW_FORMAT_DIRECT' | 'IS_LOGIC_FUNCTION_PREBUILT_MODE_ENABLED' | 'IS_SETTINGS_DISCOVERY_HERO_ENABLED' | 'IS_CALL_RECORDING_ENABLED' | 'IS_WORKFLOW_RUN_STEP_LOGS_ENABLED'
export interface WorkspaceUrls {
customUrl?: Scalars['String']
@@ -8812,6 +8812,7 @@ export const enumFeatureFlagKey = {
IS_REST_METADATA_API_NEW_FORMAT_DIRECT: 'IS_REST_METADATA_API_NEW_FORMAT_DIRECT' as const,
IS_LOGIC_FUNCTION_PREBUILT_MODE_ENABLED: 'IS_LOGIC_FUNCTION_PREBUILT_MODE_ENABLED' as const,
IS_SETTINGS_DISCOVERY_HERO_ENABLED: 'IS_SETTINGS_DISCOVERY_HERO_ENABLED' as const,
IS_CALL_RECORDING_ENABLED: 'IS_CALL_RECORDING_ENABLED' as const,
IS_WORKFLOW_RUN_STEP_LOGS_ENABLED: 'IS_WORKFLOW_RUN_STEP_LOGS_ENABLED' as const
}
@@ -283,6 +283,7 @@ export type FeatureFlag = {
};
export enum FeatureFlagKey {
IS_CALL_RECORDING_ENABLED = 'IS_CALL_RECORDING_ENABLED',
IS_EMAIL_GROUP_ENABLED = 'IS_EMAIL_GROUP_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
IS_JUNCTION_RELATIONS_ENABLED = 'IS_JUNCTION_RELATIONS_ENABLED',
@@ -1648,6 +1648,7 @@ export type FeatureFlag = {
};
export enum FeatureFlagKey {
IS_CALL_RECORDING_ENABLED = 'IS_CALL_RECORDING_ENABLED',
IS_EMAIL_GROUP_ENABLED = 'IS_EMAIL_GROUP_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
IS_JUNCTION_RELATIONS_ENABLED = 'IS_JUNCTION_RELATIONS_ENABLED',
@@ -30,7 +30,7 @@ export default meta;
export type Story = StoryObj<typeof RecordIndexPage>;
export const Default: Story = {
// oxlint-disable-next-line @typescripttypescript/ban-ts-comment
// oxlint-disable-next-line typescript/ban-ts-comment
// @ts-ignore
decorators: [LoadingDecorator, PageDecorator],
play: async ({ canvasElement }) => {
@@ -79,7 +79,7 @@ export type Story = StoryObj<typeof RecordIndexPage>;
export const Default: Story = {
parameters: userMetadataLoaderMocks,
// oxlint-disable-next-line @typescripttypescript/ban-ts-comment
// oxlint-disable-next-line typescript/ban-ts-comment
// @ts-ignore
decorators: [PageDecorator],
play: async ({ canvasElement }) => {
@@ -690,7 +690,7 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => (
<Route
element={
<SettingsProtectedRouteWrapper
settingsPermission={PermissionFlagType.AI}
settingsPermission={PermissionFlagType.AI_SETTINGS}
/>
}
>
@@ -171,7 +171,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
label: t`AI`,
path: SettingsPath.AI,
Icon: IconSparkles,
isHidden: !permissionMap[PermissionFlagType.AI],
isHidden: !permissionMap[PermissionFlagType.AI_SETTINGS],
},
{
label: t`Email`,
@@ -32,7 +32,7 @@ export const StepBar = ({ activeStep, children }: StepBarProps) => {
}
// If the child is not a Step, return it as-is
// oxlint-disable-next-line @typescripttypescript/ban-ts-comment
// oxlint-disable-next-line typescript/ban-ts-comment
// @ts-expect-error
if (child.type?.displayName !== Step.displayName) {
return child;
@@ -64,7 +64,7 @@ export type Story = StoryObj<typeof RecordShowPage>;
// TEMP_DISABLED_TEST: Temporarily commented out due to test failure
// export const Default: Story = {
// // oxlint-disable-next-line @typescripttypescript/ban-ts-comment
// // oxlint-disable-next-line typescript/ban-ts-comment
// // @ts-ignore
// decorators: [PageDecorator, ContextStoreDecorator],
// play: async ({ canvasElement }) => {
@@ -1,4 +1,4 @@
// oxlint-disable-next-line @typescripttypescript/ban-ts-comment
// oxlint-disable-next-line typescript/ban-ts-comment
// @ts-ignore
import { type WorkflowRun } from '@/workflow/types/Workflow';
import { StepStatus } from 'twenty-shared/workflow';
+77
View File
@@ -0,0 +1,77 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "typescript", "import", "unicorn"],
"jsPlugins": ["../twenty-oxlint-rules/dist/oxlint-plugin.mjs"],
"categories": {
"correctness": "off"
},
"ignorePatterns": ["node_modules", "generated", "**/*.module.scss.d.ts"],
"rules": {
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"no-console": [
"warn",
{ "allow": ["group", "groupCollapsed", "groupEnd"] }
],
"no-control-regex": "off",
"no-debugger": "error",
"no-duplicate-imports": "error",
"no-undef": "off",
"no-unused-vars": "off",
"no-redeclare": "off",
"import/no-duplicates": "error",
"react/no-unescaped-entities": "off",
"react/prop-types": "off",
"react/jsx-key": "off",
"react/display-name": "off",
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-no-useless-fragment": "off",
"react/jsx-props-no-spreading": ["error", { "explicitSpread": "ignore" }],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"typescript/no-redeclare": "error",
"typescript/ban-ts-comment": "error",
"typescript/consistent-type-imports": [
"error",
{
"prefer": "type-imports",
"fixStyle": "inline-type-imports"
}
],
"typescript/explicit-function-return-type": "off",
"typescript/explicit-module-boundary-types": "off",
"typescript/no-empty-object-type": [
"error",
{
"allowInterfaces": "with-single-extends"
}
],
"typescript/no-empty-function": "off",
"typescript/no-explicit-any": "off",
"typescript/no-unused-vars": [
"warn",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_"
}
],
"twenty/enforce-module-boundaries": [
"error",
{
"depConstraints": [
{
"sourceTag": "scope:shared",
"onlyDependOnLibsWithTags": ["scope:shared"]
}
]
}
]
}
}
+6
View File
@@ -0,0 +1,6 @@
dist
storybook-static
coverage
# Generated by vite-plugin-sass-dts (committed; regenerated in dev mode).
*.module.scss.d.ts
+78
View File
@@ -0,0 +1,78 @@
[
{
"name": ". (root barrel)",
"path": "dist/index.mjs",
"limit": "60 kB",
"import": "*"
},
{
"name": "accessibility",
"path": "dist/accessibility.mjs",
"limit": "10 kB"
},
{
"name": "assets",
"path": "dist/assets.mjs",
"limit": "10 kB"
},
{
"name": "testing",
"path": "dist/testing.mjs",
"limit": "10 kB"
},
{
"name": "components",
"path": "dist/components.mjs",
"limit": "40 kB"
},
{
"name": "display",
"path": "dist/display.mjs",
"limit": "30 kB"
},
{
"name": "feedback",
"path": "dist/feedback.mjs",
"limit": "20 kB"
},
{
"name": "input",
"path": "dist/input.mjs",
"limit": "50 kB"
},
{
"name": "layout",
"path": "dist/layout.mjs",
"limit": "20 kB"
},
{
"name": "navigation",
"path": "dist/navigation.mjs",
"limit": "20 kB"
},
{
"name": "json-visualizer",
"path": "dist/json-visualizer.mjs",
"limit": "30 kB"
},
{
"name": "theme",
"path": "dist/theme.mjs",
"limit": "55 kB"
},
{
"name": "theme-constants",
"path": "dist/theme-constants.mjs",
"limit": "20 kB"
},
{
"name": "utilities",
"path": "dist/utilities.mjs",
"limit": "10 kB"
},
{
"name": "style.css",
"path": "dist/style.css",
"limit": "40 kB"
}
]
+58
View File
@@ -0,0 +1,58 @@
import type { StorybookConfig } from '@storybook/react-vite';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import checker from 'vite-plugin-checker';
const dirname =
typeof __dirname !== 'undefined'
? __dirname
: path.dirname(fileURLToPath(import.meta.url));
const isVitest = Boolean(process.env.VITEST);
const config: StorybookConfig = {
stories: ['../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: [
'@storybook-community/storybook-addon-cookie',
'@storybook/addon-links',
'@storybook/addon-coverage',
'@storybook/addon-a11y',
'storybook-addon-pseudo-states',
'@storybook/addon-vitest',
],
framework: '@storybook/react-vite',
viteFinal: async (viteConfig) => {
const plugins = [...(viteConfig.plugins ?? [])];
if (!isVitest) {
plugins.push(
checker({
typescript: {
tsconfigPath: path.resolve(dirname, '../tsconfig.json'),
},
}),
);
}
return {
...viteConfig,
plugins,
resolve: {
...viteConfig.resolve,
alias: {
...(viteConfig.resolve?.alias ?? {}),
'@tabler/icons-react': '@tabler/icons-react/dist/esm/icons/index.mjs',
},
},
};
},
};
export default config;
// To customize your Vite configuration you can use the viteFinal field.
// Check https://storybook.js.org/docs/react/builders/vite#configuration
// and https://nx.dev/recipes/storybook/custom-builder-configs
@@ -0,0 +1,34 @@
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"
rel="stylesheet"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.7/iframeResizer.contentWindow.min.js"></script>
<style type="text/css">
body {
margin: 0;
font-family: 'Inter', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
html {
font-size: 13px;
}
.sbdocs-wrapper {
padding: 0 !important;
}
*::-webkit-scrollbar {
height: 4px;
width: 4px;
}
*::-webkit-scrollbar-corner {
background-color: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: transparent;
border-radius: 2px;
}
</style>
@@ -0,0 +1,24 @@
import { type Preview } from '@storybook/react-vite';
import '@new-ui/theme-constants/theme-light.css';
import '@new-ui/theme-constants/theme-dark.css';
import { ThemeProvider } from '@new-ui/theme-constants';
const preview: Preview = {
tags: ['autodocs'],
parameters: {
a11y: {
test: 'error',
},
},
decorators: [
(Story) => {
return (
<ThemeProvider colorScheme="light">
<Story />
</ThemeProvider>
);
},
],
};
export default preview;
@@ -0,0 +1,5 @@
import { setProjectAnnotations } from '@storybook/react-vite';
import * as projectAnnotations from './preview';
// Apply Storybook's preview configuration to Vitest runs.
setProjectAnnotations([projectAnnotations]);
+13 -57
View File
@@ -1,6 +1,10 @@
# twenty-new-ui
> **Status:** Planning. This is a design document; no implementation has started.
> **Status:** Phase 0 — Foundations in progress. The package is scaffolded and builds
> on SCSS Modules + Base UI; the theme layer is ported from `twenty-ui` with a parity
> test, and the size/Storybook/a11y harnesses are wired up. Remaining Phase 0 work: the
> CI diff-table workflow, the `twenty-ui` component inventory, and the `modules/ui` triage.
> The sections below remain the design document for the full effort.
`twenty-new-ui` is the next generation of Twenty's UI library, replacing [`twenty-ui`](../twenty-ui).
It is built on a headless component library and a zero-runtime, CSS-variable styling layer.
@@ -30,8 +34,9 @@ components are now **in scope** — they migrate into `twenty-new-ui` (see [Appl
## Decision 1 — Headless library: Base UI
Adopt **Base UI** (`@base-ui-components/react`, MIT) as the behavioral foundation; build Twenty's
visual design on top of it.
Adopt **Base UI** ([`mui/base-ui`](https://github.com/mui/base-ui), published to npm as
[`@base-ui/react`](https://base-ui.com), MIT) as the behavioral foundation; build Twenty's visual
design on top of it.
| | Base UI | shadcn/ui | Radix |
| --- | --- | --- | --- |
@@ -84,7 +89,7 @@ packages/twenty-new-ui/
├── vitest.config.ts # storybook component tests
├── .storybook/
├── .size-limit.json # per-entry bundle budgets
├── scripts/ # generateBarrels.ts, generateTheme.ts
├── scripts/ # generateBarrels.ts
└── src/
├── styles/ # global: reset, theme vars, mixins, breakpoints
├── theme/ theme-constants/
@@ -106,8 +111,8 @@ as-is.
- Keep the public API identical: `ThemeProvider`, `ThemeContext`, `useTheme`, the `themeCssVariables` shape, `ThemeType`, color helpers, and the `theme-light.css` / `theme-dark.css` exports.
- Reuse `twenty-ui`'s token values verbatim to guarantee identical design.
- Tokens are authored in `src/theme/` and generated into CSS variables + a typed accessor.
- A generated-output diff test asserts the new theme CSS produces the same `--t-*` values as `twenty-ui`.
- Tokens live in `src/theme/` (`THEME_LIGHT` / `THEME_DARK`); the `--t-*` CSS variables and the `themeCssVariables` accessor are static files mirrored token-for-token from `twenty-ui` (matching `twenty-ui`'s own static-CSS approach).
- A theme parity test asserts the theme CSS and `themeCssVariables` stay identical to `twenty-ui`'s `--t-*` values.
## Component migration map
@@ -219,7 +224,7 @@ CI surfaces a per-PR diff table (`twenty-ui` vs `twenty-new-ui`) for size, a11y,
- Vite library mode, dual ESM/CJS, `vite-plugin-dts`, `vite-plugin-svgr`; SCSS via Vite's built-in `sass`; no Babel.
- `sideEffects: ["**/*.css", "**/*.scss"]`; emit per-entry CSS plus `style.css` / `theme-light.css` / `theme-dark.css`.
- Public package (remove `private`); scoped name (e.g. `@twenty/ui`).
- Public package (remove `private`); ships as `twenty-new-ui` until cut-over, then claims the `twenty-ui` name once the old package is removed.
- **Changesets** for semver + changelog; GitHub Actions release with `npm publish --provenance`.
- Declare `react` / `react-dom` as peer dependencies; validate the `exports`/types map with `publint` + `@arethetypeswrong/cli`.
- Publish the Storybook as living documentation.
@@ -259,59 +264,10 @@ a passing visual-parity diff, and a within-budget size entry.
## Open questions
1. Published package name/scope (proposed `@twenty/ui`).
1. Published package name: `twenty-new-ui` now, renamed to `twenty-ui` at cut-over (Phase 6).
2. Styling: confirm SCSS Modules vs vanilla-extract vs plain CSS Modules.
3. Variants helper: `clsx` + `data-*` vs `cva`.
4. Visual regression tooling: Chromatic vs self-hosted image snapshots.
5. How aggressively to drop `framer-motion` in favor of CSS/Base UI transitions.
6. Scope of `assets` / `testing` / `json-visualizer`: port verbatim or modernize.
7. Where to draw the generic-vs-app-specific line for `modules/ui`, and whether hybrid components live as a headless core in `twenty-new-ui` with a thin app wrapper in `twenty-front`.
## Appendix — example component pattern
`src/input/components/Toggle.tsx`
```tsx
import { Switch } from '@base-ui-components/react/switch';
import { clsx } from 'clsx';
import styles from './Toggle.module.scss';
type ToggleProps = {
value?: boolean;
onChange?: (value: boolean) => void;
disabled?: boolean;
size?: 'small' | 'medium';
};
export const Toggle = ({ value, onChange, disabled, size = 'medium' }: ToggleProps) => (
<Switch.Root
checked={value}
onCheckedChange={onChange}
disabled={disabled}
className={clsx(styles.root, styles[size])}
>
<Switch.Thumb className={styles.thumb} />
</Switch.Root>
);
```
`src/input/components/Toggle.module.scss`
```scss
.root {
background: var(--t-background-quaternary);
border-radius: var(--t-border-radius-pill);
transition: background var(--t-animation-duration-fast) ease;
&[data-checked] { background: var(--t-color-blue); }
&[data-disabled] { opacity: 0.32; cursor: not-allowed; }
}
.thumb {
background: var(--t-background-primary);
border-radius: 50%;
transition: translate var(--t-animation-duration-fast) ease;
.root[data-checked] & { translate: 100% 0; }
}
```
@@ -0,0 +1 @@
export default 'test-file-stub';
@@ -0,0 +1,7 @@
// Proxy so `styles.anyClassName` resolves to its key when SCSS is imported in Jest.
module.exports = new Proxy(
{},
{
get: (_target, key) => (key === '__esModule' ? false : String(key)),
},
);
+41
View File
@@ -0,0 +1,41 @@
import { readFileSync } from 'fs';
import { dirname, resolve } from 'path';
import { pathsToModuleNameMapper } from 'ts-jest';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const tsConfigPath = resolve(__dirname, './tsconfig.json');
const tsConfig = JSON.parse(readFileSync(tsConfigPath, 'utf8'));
const jestConfig = {
displayName: 'twenty-new-ui',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['./setupTests.ts'],
testEnvironment: 'jsdom',
transformIgnorePatterns: ['../../node_modules/'],
transform: {
'^.+\\.[tj]sx?$': [
'@swc/jest',
{
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'automatic' } },
},
},
],
},
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|webp|svg|svg)$': '<rootDir>/__mocks__/imageMockUi.js',
'\\.(scss|css)$': '<rootDir>/__mocks__/styleMock.js',
...pathsToModuleNameMapper(tsConfig.compilerOptions.paths, {
prefix: '<rootDir>/',
}),
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
extensionsToTreatAsEsm: ['.ts', '.tsx'],
coverageDirectory: './coverage',
};
export default jestConfig;
+208
View File
@@ -0,0 +1,208 @@
{
"name": "twenty-new-ui",
"//name": "Named `twenty-new-ui` because the target name `twenty-ui` is still taken by the existing packages/twenty-ui workspace (Yarn forbids duplicate workspace names). Renamed to `twenty-ui` at the Phase 6 cut-over once the old package is removed.",
"version": "0.0.0",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"style": "./dist/style.css",
"type": "module",
"sideEffects": [
"**/*.css",
"**/*.scss"
],
"devDependencies": {
"@argos-ci/storybook": "^6.0.6",
"@prettier/sync": "^0.5.2",
"@size-limit/preset-small-lib": "^11.1.6",
"@storybook-community/storybook-addon-cookie": "^5.0.0",
"@storybook/addon-a11y": "^10.3.3",
"@storybook/addon-coverage": "^3.0.0",
"@storybook/addon-links": "^10.3.3",
"@storybook/addon-vitest": "^10.2.13",
"@storybook/react-vite": "^10.3.3",
"@swc/core": "^1.15.11",
"@swc/jest": "^0.2.39",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@types/jest": "^30.0.0",
"@types/react": "^18.2.39",
"@types/react-dom": "^18.2.15",
"@vitejs/plugin-react-swc": "4.2.3",
"@vitest/browser-playwright": "4.0.18",
"jest": "29.7.0",
"jest-environment-jsdom": "30.0.0-beta.3",
"prettier": "^3.1.1",
"sass": "^1.83.0",
"sass-embedded": "^1.83.0",
"size-limit": "^11.1.6",
"slash": "^5.1.0",
"storybook-addon-pseudo-states": "^10.3.3",
"ts-jest": "^29.1.1",
"tsx": "^4.19.3",
"vite-plugin-checker": "^0.10.2",
"vite-plugin-dts": "3.8.1",
"vite-plugin-sass-dts": "^1.3.31",
"vite-plugin-svgr": "^4.3.0",
"vite-tsconfig-paths": "^4.2.1",
"vitest": "4.0.18"
},
"dependencies": {
"@base-ui/react": "^1.5.0",
"@monaco-editor/react": "^4.7.0",
"@radix-ui/colors": "^3.0.0",
"@tabler/icons-react": "^3.31.0",
"clsx": "^2.1.1",
"date-fns": "^2.30.0",
"framer-motion": "^11.18.0",
"glob": "^11.1.0",
"hex-rgb": "^5.0.0",
"jotai": "^2.17.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-responsive": "^9.0.2",
"react-router-dom": "^6.4.4",
"twenty-shared": "workspace:*",
"zod": "^4.1.11"
},
"peerDependencies": {
"monaco-editor": ">= 0.25.0 < 1"
},
"scripts": {
"build": "npx vite build"
},
"files": [
"dist",
"accessibility",
"assets",
"components",
"display",
"feedback",
"input",
"json-visualizer",
"layout",
"navigation",
"testing",
"theme",
"theme-constants",
"utilities"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./style.css": "./dist/style.css",
"./theme-light.css": "./dist/theme-light.css",
"./theme-dark.css": "./dist/theme-dark.css",
"./accessibility": {
"types": "./dist/accessibility/index.d.ts",
"import": "./dist/accessibility.mjs",
"require": "./dist/accessibility.cjs"
},
"./assets": {
"types": "./dist/assets/index.d.ts",
"import": "./dist/assets.mjs",
"require": "./dist/assets.cjs"
},
"./components": {
"types": "./dist/components/index.d.ts",
"import": "./dist/components.mjs",
"require": "./dist/components.cjs"
},
"./display": {
"types": "./dist/display/index.d.ts",
"import": "./dist/display.mjs",
"require": "./dist/display.cjs"
},
"./feedback": {
"types": "./dist/feedback/index.d.ts",
"import": "./dist/feedback.mjs",
"require": "./dist/feedback.cjs"
},
"./input": {
"types": "./dist/input/index.d.ts",
"import": "./dist/input.mjs",
"require": "./dist/input.cjs"
},
"./json-visualizer": {
"types": "./dist/json-visualizer/index.d.ts",
"import": "./dist/json-visualizer.mjs",
"require": "./dist/json-visualizer.cjs"
},
"./layout": {
"types": "./dist/layout/index.d.ts",
"import": "./dist/layout.mjs",
"require": "./dist/layout.cjs"
},
"./navigation": {
"types": "./dist/navigation/index.d.ts",
"import": "./dist/navigation.mjs",
"require": "./dist/navigation.cjs"
},
"./testing": {
"types": "./dist/testing/index.d.ts",
"import": "./dist/testing.mjs",
"require": "./dist/testing.cjs"
},
"./theme": {
"types": "./dist/theme/index.d.ts",
"import": "./dist/theme.mjs",
"require": "./dist/theme.cjs"
},
"./theme-constants": {
"types": "./dist/theme-constants/index.d.ts",
"import": "./dist/theme-constants.mjs",
"require": "./dist/theme-constants.cjs"
},
"./utilities": {
"types": "./dist/utilities/index.d.ts",
"import": "./dist/utilities.mjs",
"require": "./dist/utilities.cjs"
}
},
"typesVersions": {
"*": {
"accessibility": [
"dist/accessibility/index.d.ts"
],
"assets": [
"dist/assets/index.d.ts"
],
"components": [
"dist/components/index.d.ts"
],
"display": [
"dist/display/index.d.ts"
],
"feedback": [
"dist/feedback/index.d.ts"
],
"input": [
"dist/input/index.d.ts"
],
"json-visualizer": [
"dist/json-visualizer/index.d.ts"
],
"layout": [
"dist/layout/index.d.ts"
],
"navigation": [
"dist/navigation/index.d.ts"
],
"testing": [
"dist/testing/index.d.ts"
],
"theme": [
"dist/theme/index.d.ts"
],
"theme-constants": [
"dist/theme-constants/index.d.ts"
],
"utilities": [
"dist/utilities/index.d.ts"
]
}
}
}
+103
View File
@@ -0,0 +1,103 @@
{
"name": "twenty-new-ui",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/twenty-new-ui/src",
"projectType": "library",
"tags": ["scope:shared"],
"targets": {
"build": {
"dependsOn": ["^build"],
"outputs": [
"{projectRoot}/dist",
"{projectRoot}/accessibility/package.json",
"{projectRoot}/accessibility/dist",
"{projectRoot}/assets/package.json",
"{projectRoot}/assets/dist",
"{projectRoot}/components/package.json",
"{projectRoot}/components/dist",
"{projectRoot}/display/package.json",
"{projectRoot}/display/dist",
"{projectRoot}/feedback/package.json",
"{projectRoot}/feedback/dist",
"{projectRoot}/input/package.json",
"{projectRoot}/input/dist",
"{projectRoot}/json-visualizer/package.json",
"{projectRoot}/json-visualizer/dist",
"{projectRoot}/layout/package.json",
"{projectRoot}/layout/dist",
"{projectRoot}/navigation/package.json",
"{projectRoot}/navigation/dist",
"{projectRoot}/testing/package.json",
"{projectRoot}/testing/dist",
"{projectRoot}/theme/package.json",
"{projectRoot}/theme/dist",
"{projectRoot}/theme-constants/package.json",
"{projectRoot}/theme-constants/dist",
"{projectRoot}/utilities/package.json",
"{projectRoot}/utilities/dist"
]
},
"generateBarrels": {
"executor": "nx:run-commands",
"cache": true,
"inputs": ["production", "{projectRoot}/scripts/generateBarrels.ts"],
"outputs": [
"{projectRoot}/src/**/*/index.ts",
"{projectRoot}/package.json"
],
"options": { "command": "tsx {projectRoot}/scripts/generateBarrels.ts" }
},
"build:individual": {
"executor": "nx:run-commands",
"cache": true,
"dependsOn": ["build"],
"inputs": ["production", "^production"],
"outputs": ["{projectRoot}/dist/individual"],
"options": {
"cwd": "{projectRoot}",
"command": "npx vite build -c vite.config.individual.ts"
}
},
"size": {
"executor": "nx:run-commands",
"cache": true,
"dependsOn": ["build"],
"inputs": ["{projectRoot}/dist", "{projectRoot}/.size-limit.json"],
"options": {
"cwd": "{projectRoot}",
"command": "npx size-limit"
},
"configurations": {
"why": { "command": "npx size-limit --why" }
}
},
"clean": {
"executor": "nx:run-commands",
"options": { "command": "rimraf {projectRoot}/dist" }
},
"lint": {},
"fmt": { "options": { "files": "src" }, "configurations": { "fix": {} } },
"test": {},
"typecheck": {},
"storybook:build": { "configurations": { "test": {} } },
"storybook:serve:dev": { "options": { "port": 6008 } },
"storybook:serve:static": {
"options": {
"buildTarget": "twenty-new-ui:storybook:build",
"port": 6008
},
"configurations": { "test": {} }
},
"storybook:test": {},
"storybook:test:no-coverage": {},
"storybook:visual-diff": {
"executor": "nx:run-commands",
"dependsOn": ["storybook:build"],
"options": {
"cwd": "{projectRoot}",
"command": "bash scripts/visual-diff.sh"
}
},
"storybook:coverage": {}
}
}
@@ -0,0 +1,505 @@
import prettier from '@prettier/sync';
import * as fs from 'fs';
import { globSync } from 'glob';
import path from 'path';
import { type Options } from 'prettier';
import slash from 'slash';
import ts from 'typescript';
// TODO prastoin refactor this file in several one into its dedicated package and make it a TypeScript CLI
const INDEX_FILENAME = 'index';
const PACKAGE_JSON_FILENAME = 'package.json';
const NX_PROJECT_CONFIGURATION_FILENAME = 'project.json';
const PACKAGE_PATH = path.resolve('packages/twenty-ui');
const SRC_PATH = path.resolve(`${PACKAGE_PATH}/src`);
const PACKAGE_JSON_PATH = path.join(PACKAGE_PATH, PACKAGE_JSON_FILENAME);
const NX_PROJECT_CONFIGURATION_PATH = path.join(
PACKAGE_PATH,
NX_PROJECT_CONFIGURATION_FILENAME,
);
const prettierConfigFile = prettier.resolveConfigFile();
if (prettierConfigFile == null) {
throw new Error('Prettier config file not found');
}
const prettierConfiguration = prettier.resolveConfig(prettierConfigFile);
const prettierFormat = (str: string, parser: Options['parser']) =>
prettier.format(str, {
...prettierConfiguration,
parser,
});
type createTypeScriptFileArgs = {
path: string;
content: string;
filename: string;
};
const createTypeScriptFile = ({
content,
path: filePath,
filename,
}: createTypeScriptFileArgs) => {
const header = `
/*
* _____ _
*|_ _|_ _____ _ __ | |_ _ _
* | | \\ \\ /\\ / / _ \\ '_ \\| __| | | | Auto-generated file
* | | \\ V V / __/ | | | |_| |_| | Any edits to this will be overridden
* |_| \\_/\\_/ \\___|_| |_|\\__|\\__, |
* |___/
*/
`;
const formattedContent = prettierFormat(
`${header}\n${content}\n`,
'typescript',
);
fs.writeFileSync(
path.join(filePath, `${filename}.ts`),
formattedContent,
'utf-8',
);
};
const getLastPathFolder = (pathStr: string) => path.basename(pathStr);
const getSubDirectoryPaths = (directoryPath: string): string[] => {
const pattern = slash(path.join(directoryPath, '*/'));
return globSync(pattern, {
ignore: [...EXCLUDED_DIRECTORIES],
cwd: SRC_PATH,
nodir: false,
maxDepth: 1,
}).sort((a, b) => a.localeCompare(b));
};
const partitionFileExportsByType = (declarations: DeclarationOccurrence[]) => {
return declarations.reduce<{
typeAndInterfaceDeclarations: DeclarationOccurrence[];
otherDeclarations: DeclarationOccurrence[];
}>(
(acc, { kind, name }) => {
if (kind === 'type' || kind === 'interface') {
return {
...acc,
typeAndInterfaceDeclarations: [
...acc.typeAndInterfaceDeclarations,
{ kind, name },
],
};
}
return {
...acc,
otherDeclarations: [...acc.otherDeclarations, { kind, name }],
};
},
{
typeAndInterfaceDeclarations: [],
otherDeclarations: [],
},
);
};
const generateModuleIndexFiles = (exportByBarrel: ExportByBarrel[]) => {
return exportByBarrel.map<createTypeScriptFileArgs>(
({ barrel: { moduleDirectory }, allFileExports }) => {
const content = allFileExports
.sort((a, b) => a.file.localeCompare(b.file))
.map(({ exports, file }) => {
const { otherDeclarations, typeAndInterfaceDeclarations } =
partitionFileExportsByType(exports);
const fileWithoutExtension = path.parse(file).name;
const pathToImport = slash(
path.relative(
moduleDirectory,
path.join(path.dirname(file), fileWithoutExtension),
),
);
const mapDeclarationNameAndJoin = (
declarations: DeclarationOccurrence[],
) => declarations.map(({ name }) => name).join(', ');
const typeExport =
typeAndInterfaceDeclarations.length > 0
? `export type { ${mapDeclarationNameAndJoin(typeAndInterfaceDeclarations)} } from "./${pathToImport}"`
: '';
const othersExport =
otherDeclarations.length > 0
? `export { ${mapDeclarationNameAndJoin(otherDeclarations)} } from "./${pathToImport}"`
: '';
return [typeExport, othersExport]
.filter((el) => el !== '')
.join('\n');
})
.join('\n');
return {
content,
path: moduleDirectory,
filename: INDEX_FILENAME,
};
},
);
};
type JsonUpdate = Record<string, any>;
type WriteInJsonFileArgs = {
content: JsonUpdate;
file: string;
};
const updateJsonFile = ({ content, file }: WriteInJsonFileArgs) => {
const updatedJsonFile = JSON.stringify(content);
const formattedContent = prettier.format(updatedJsonFile, {
...prettierConfiguration,
filepath: file,
});
fs.writeFileSync(file, formattedContent, 'utf-8');
};
const writeInPackageJson = (update: JsonUpdate) => {
const rawJsonFile = fs.readFileSync(PACKAGE_JSON_PATH, 'utf-8');
const initialJsonFile = JSON.parse(rawJsonFile);
updateJsonFile({
file: PACKAGE_JSON_PATH,
content: {
...initialJsonFile,
...update,
},
});
};
const updateNxProjectConfigurationBuildOutputs = (outputs: JsonUpdate) => {
const rawJsonFile = fs.readFileSync(NX_PROJECT_CONFIGURATION_PATH, 'utf-8');
const initialJsonFile = JSON.parse(rawJsonFile);
updateJsonFile({
file: NX_PROJECT_CONFIGURATION_PATH,
content: {
...initialJsonFile,
targets: {
...initialJsonFile.targets,
build: {
...initialJsonFile.targets.build,
outputs,
},
},
},
});
};
type ExportOccurrence = {
types: string;
import: string;
require: string;
};
type ExportsConfig = Record<string, ExportOccurrence | string>;
const generateModulePackageExports = (moduleDirectories: string[]) => {
return moduleDirectories.reduce<ExportsConfig>(
(acc, moduleDirectory) => {
const moduleName = getLastPathFolder(moduleDirectory);
if (moduleName === undefined) {
throw new Error(
`Should never occur, moduleName is undefined ${moduleDirectory}`,
);
}
return {
...acc,
[`./${moduleName}`]: {
types: `./dist/${moduleName}/index.d.ts`,
import: `./dist/${moduleName}.mjs`,
require: `./dist/${moduleName}.cjs`,
},
};
},
{
'./style.css': './dist/style.css',
'./theme-light.css': './dist/theme-light.css',
'./theme-dark.css': './dist/theme-dark.css',
},
);
};
const computePackageJsonFilesAndExportsConfig = (
moduleDirectories: string[],
) => {
const entrypoints = moduleDirectories.map(getLastPathFolder);
const exports = {
'.': {
types: './dist/index.d.ts',
import: './dist/index.mjs',
require: './dist/index.cjs',
},
...generateModulePackageExports(moduleDirectories),
} satisfies ExportsConfig;
const typesVersionsEntries = entrypoints.reduce<Record<string, string[]>>(
(acc, moduleName) => ({
...acc,
[`${moduleName}`]: [`dist/${moduleName}/index.d.ts`],
}),
{},
);
return {
exports,
typesVersions: { '*': typesVersionsEntries },
files: ['dist', ...entrypoints],
};
};
const computeProjectNxBuildOutputsPath = (moduleDirectories: string[]) => {
const dynamicOutputsPath = moduleDirectories
.map(getLastPathFolder)
.flatMap((barrelName) =>
['package.json', 'dist'].map(
(subPath) => `{projectRoot}/${barrelName}/${subPath}`,
),
);
return ['{projectRoot}/dist', ...dynamicOutputsPath];
};
const EXCLUDED_EXTENSIONS = [
'**/*.test.ts',
'**/*.test.tsx',
'**/*.spec.ts',
'**/*.spec.tsx',
'**/*.stories.ts',
'**/*.stories.tsx',
] as const;
const EXCLUDED_DIRECTORIES = [
'**/__tests__/**',
'**/__mocks__/**',
'**/__stories__/**',
'**/internal/**',
] as const;
function getTypeScriptFiles(
directoryPath: string,
includeIndex: boolean = false,
): string[] {
const pattern = slash(path.join(directoryPath, '**', '*.{ts,tsx}'));
const files = globSync(pattern, {
cwd: SRC_PATH,
nodir: true,
ignore: [...EXCLUDED_EXTENSIONS, ...EXCLUDED_DIRECTORIES],
});
return files.filter(
(file) =>
!file.endsWith('.d.ts') &&
(includeIndex ? true : !file.endsWith('index.ts')),
);
}
const getKind = (
node: ts.VariableStatement,
): Extract<ExportKind, 'const' | 'let' | 'var'> => {
const isConst = (node.declarationList.flags & ts.NodeFlags.Const) !== 0;
if (isConst) {
return 'const';
}
const isLet = (node.declarationList.flags & ts.NodeFlags.Let) !== 0;
if (isLet) {
return 'let';
}
return 'var';
};
function extractExportsFromSourceFile(sourceFile: ts.SourceFile) {
const exports: DeclarationOccurrence[] = [];
function visit(node: ts.Node) {
if (!ts.canHaveModifiers(node)) {
return ts.forEachChild(node, visit);
}
const modifiers = ts.getModifiers(node);
const isExport = modifiers?.some(
(mod) => mod.kind === ts.SyntaxKind.ExportKeyword,
);
if (!isExport && !ts.isExportDeclaration(node)) {
return ts.forEachChild(node, visit);
}
switch (true) {
case ts.isTypeAliasDeclaration(node):
exports.push({
kind: 'type',
name: node.name.text,
});
break;
case ts.isInterfaceDeclaration(node):
exports.push({
kind: 'interface',
name: node.name.text,
});
break;
case ts.isEnumDeclaration(node):
exports.push({
kind: 'enum',
name: node.name.text,
});
break;
case ts.isFunctionDeclaration(node) && node.name !== undefined:
exports.push({
kind: 'function',
name: node.name.text,
});
break;
case ts.isVariableStatement(node):
node.declarationList.declarations.forEach((decl) => {
const kind = getKind(node);
if (ts.isIdentifier(decl.name)) {
exports.push({
kind,
name: decl.name.text,
});
} else if (ts.isObjectBindingPattern(decl.name)) {
decl.name.elements.forEach((element) => {
if (
!ts.isBindingElement(element) ||
!ts.isIdentifier(element.name)
) {
return;
}
exports.push({
kind,
name: element.name.text,
});
});
}
});
break;
case ts.isClassDeclaration(node) && node.name !== undefined:
exports.push({
kind: 'class',
name: node.name.text,
});
break;
case ts.isExportDeclaration(node):
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
node.exportClause.elements.forEach((element) => {
const exportName = element.name.text;
// Check both the declaration and the individual specifier for type-only exports
const isTypeExport =
node.isTypeOnly || ts.isTypeOnlyExportDeclaration(node);
if (isTypeExport) {
// should handle kind
exports.push({
kind: 'type',
name: exportName,
});
return;
}
exports.push({
kind: 'const',
name: exportName,
});
});
}
break;
}
return ts.forEachChild(node, visit);
}
visit(sourceFile);
return exports;
}
type ExportKind =
| 'type'
| 'interface'
| 'enum'
| 'function'
| 'const'
| 'let'
| 'var'
| 'class';
type DeclarationOccurrence = { kind: ExportKind; name: string };
type FileExports = Array<{
file: string;
exports: DeclarationOccurrence[];
}>;
function findAllExports(directoryPath: string): FileExports {
const results: FileExports = [];
const files = getTypeScriptFiles(directoryPath);
for (const file of files) {
const sourceFile = ts.createSourceFile(
file,
fs.readFileSync(file, 'utf8'),
ts.ScriptTarget.Latest,
true,
);
const exports = extractExportsFromSourceFile(sourceFile);
if (exports.length > 0) {
results.push({
file,
exports,
});
}
}
return results;
}
type ExportByBarrel = {
barrel: {
moduleName: string;
moduleDirectory: string;
};
allFileExports: FileExports;
};
const retrieveExportsByBarrel = (barrelDirectories: string[]) => {
return barrelDirectories.map<ExportByBarrel>((moduleDirectory) => {
const moduleExportsPerFile = findAllExports(moduleDirectory);
const moduleName = getLastPathFolder(moduleDirectory);
if (!moduleName) {
throw new Error(
`Should never occur moduleName not found ${moduleDirectory}`,
);
}
return {
barrel: {
moduleName,
moduleDirectory,
},
allFileExports: moduleExportsPerFile,
};
});
};
const main = () => {
const moduleDirectories = getSubDirectoryPaths(SRC_PATH);
const exportsByBarrel = retrieveExportsByBarrel(moduleDirectories);
const moduleIndexFiles = generateModuleIndexFiles(exportsByBarrel);
const packageJsonConfig =
computePackageJsonFilesAndExportsConfig(moduleDirectories);
const nxBuildOutputsPath =
computeProjectNxBuildOutputsPath(moduleDirectories);
updateNxProjectConfigurationBuildOutputs(nxBuildOutputsPath);
writeInPackageJson(packageJsonConfig);
moduleIndexFiles.forEach(createTypeScriptFile);
};
main();
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [[ -f "$SCRIPT_DIR/.env" ]]; then
set -a
source "$SCRIPT_DIR/.env"
set +a
fi
ARGOS_API_BASE_URL="${ARGOS_API_BASE_URL:-http://127.0.0.1:4002/v2/}"
ARGOS_TOKEN="${ARGOS_TOKEN:?ARGOS_TOKEN is required set it in packages/twenty-new-ui/.env}"
USERNAME=$(whoami)
GIT_BRANCH="${ARGOS_BRANCH:-$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")}"
COMMIT="${ARGOS_COMMIT:-$(git rev-parse HEAD 2>/dev/null || echo "unknown")}"
REFERENCE_COMMIT="${ARGOS_REFERENCE_COMMIT:-$(git merge-base HEAD main 2>/dev/null || echo "")}"
export ARGOS_API_BASE_URL
export ARGOS_TOKEN
export ARGOS_BUILD_NAME="${USERNAME}/twenty-new-ui"
export ARGOS_BRANCH="${USERNAME}/${GIT_BRANCH}"
export ARGOS_COMMIT="$COMMIT"
export ARGOS_REFERENCE_COMMIT="${REFERENCE_COMMIT}"
echo "Argos visual diff"
echo " API: $ARGOS_API_BASE_URL"
echo " Build name: $ARGOS_BUILD_NAME"
echo " Branch: $ARGOS_BRANCH"
echo " Commit: ${ARGOS_COMMIT:0:12}"
echo " Ref commit: ${ARGOS_REFERENCE_COMMIT:0:12}"
echo ""
npx http-server storybook-static --port 6007 --silent &
HTTP_PID=$!
trap "kill $HTTP_PID 2>/dev/null || true" EXIT
SERVER_UP=false
for i in $(seq 1 30); do
if curl -sf http://localhost:6007 > /dev/null 2>&1; then SERVER_UP=true; break; fi
sleep 1
done
if [[ "$SERVER_UP" != "true" ]]; then
echo "Storybook static server did not start on http://localhost:6007 after 30s" >&2
exit 1
fi
export STORYBOOK_URL="http://localhost:6007"
npx vitest run
+5
View File
@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
+7
View File
@@ -0,0 +1,7 @@
declare global {
interface Window {
_env_?: Record<string, string>;
}
}
export {};
+4
View File
@@ -0,0 +1,4 @@
// Side-effect import: the reset must ship exactly once in the aggregated style.css.
import './styles/base/reset.scss';
export {};
@@ -0,0 +1,15 @@
// Entry point for the individual/self-contained build (vite.config.individual.ts).
// Re-exports all public modules so a single bundle contains every component with
// internal deps bundled, while React stays external for the consumer's bundler.
export * from './accessibility';
export * from './components';
export * from './display';
export * from './feedback';
export * from './input';
export * from './json-visualizer';
export * from './layout';
export * from './navigation';
export * from './theme';
export * from './theme-constants';
export * from './utilities';
@@ -0,0 +1,64 @@
.root {
position: relative;
display: flex;
flex-shrink: 0;
align-items: center;
align-self: flex-start;
border-radius: 10px;
background-color: var(--t-background-transparent-medium);
cursor: pointer;
transition: background-color duration(normal) ease;
&[data-checked] {
background-color: var(--toggle-on-color, var(--t-color-blue));
}
&[data-disabled] {
opacity: 0.5;
pointer-events: none;
}
}
.centered {
align-self: center;
}
.small {
width: 24px;
height: 16px;
}
.medium {
width: 32px;
height: 20px;
}
.thumb {
position: absolute;
top: 50%;
left: 0;
display: block;
border-radius: 50%;
background-color: var(--t-background-primary);
transition: transform duration(normal) ease;
}
.small .thumb {
width: 12px;
height: 12px;
transform: translate(2px, -50%);
}
.medium .thumb {
width: 16px;
height: 16px;
transform: translate(2px, -50%);
}
.small[data-checked] .thumb {
transform: translate(10px, -50%);
}
.medium[data-checked] .thumb {
transform: translate(14px, -50%);
}
@@ -0,0 +1,8 @@
declare const classNames: {
readonly root: 'root';
readonly centered: 'centered';
readonly small: 'small';
readonly medium: 'medium';
readonly thumb: 'thumb';
};
export default classNames;
@@ -0,0 +1,50 @@
import { type Meta, type StoryObj } from '@storybook/react-vite';
import { useState } from 'react';
import { ThemeProvider } from '@new-ui/theme-constants';
import { Toggle } from './Toggle';
const meta: Meta<typeof Toggle> = {
title: 'Input/Toggle',
component: Toggle,
args: { toggleSize: 'medium', 'aria-label': 'Example toggle' },
};
export default meta;
type Story = StoryObj<typeof Toggle>;
export const On: Story = { args: { value: true } };
export const Off: Story = { args: { value: false } };
export const Small: Story = { args: { value: true, toggleSize: 'small' } };
export const CustomColor: Story = {
args: { value: true, color: 'color(display-p3 0.2 0.7 0.4)' },
};
export const Disabled: Story = { args: { value: true, disabled: true } };
const ControlledToggle = () => {
const [value, setValue] = useState(false);
return (
<Toggle value={value} onChange={setValue} aria-label="Example toggle" />
);
};
export const Interactive: Story = {
render: () => <ControlledToggle />,
};
export const Dark: Story = {
args: { value: true },
decorators: [
(Story) => (
<ThemeProvider colorScheme="dark">
<Story />
</ThemeProvider>
),
],
};
@@ -0,0 +1,54 @@
import { Switch } from '@base-ui/react/switch';
import { clsx } from 'clsx';
import styles from './Toggle.module.scss';
export type ToggleSize = 'small' | 'medium';
export type ToggleProps = {
id?: string;
value?: boolean;
onChange?: (value: boolean, e?: React.MouseEvent<HTMLDivElement>) => void;
color?: string;
toggleSize?: ToggleSize;
className?: string;
centered?: boolean;
disabled?: boolean;
'aria-label'?: string;
'aria-labelledby'?: string;
};
export const Toggle = ({
id,
value = false,
onChange,
color,
toggleSize = 'medium',
className,
centered,
disabled,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
}: ToggleProps) => (
<Switch.Root
id={id}
checked={value}
disabled={disabled}
onCheckedChange={(checked) => onChange?.(checked)}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
className={clsx(
styles.root,
styles[toggleSize],
centered && styles.centered,
className,
)}
style={
color
? ({ '--toggle-on-color': color } as React.CSSProperties)
: undefined
}
>
<Switch.Thumb className={styles.thumb} />
</Switch.Root>
);
@@ -0,0 +1,2 @@
export { Toggle } from './components/Toggle';
export type { ToggleProps, ToggleSize } from './components/Toggle';
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
@@ -0,0 +1,18 @@
@use 'sass:map';
// Mirrors twenty-ui's MOBILE_VIEWPORT (768px).
$breakpoints: (
'mobile': 768px,
);
@mixin respond-to($name) {
$value: map.get($breakpoints, $name);
@if $value == null {
@error 'Unknown breakpoint: #{$name}';
}
@media (max-width: $value) {
@content;
}
}
@@ -0,0 +1,5 @@
// duration(fast) => calc(var(--t-animation-duration-fast) * 1s)
// Theme animation durations are stored unitless; this scales them to seconds.
@function duration($name) {
@return calc(var(--t-animation-duration-#{$name}) * 1s);
}
@@ -0,0 +1,6 @@
@mixin focus-ring {
&:focus-visible {
outline: 2px solid var(--t-color-blue);
outline-offset: 1px;
}
}
@@ -0,0 +1,15 @@
*,
*::before,
*::after {
box-sizing: border-box;
}
button {
margin: 0;
padding: 0;
font: inherit;
color: inherit;
background: none;
border: none;
cursor: pointer;
}
@@ -0,0 +1,2 @@
// Auto-generated barrel placeholder — populated as components migrate into this entry point.
export {};
@@ -0,0 +1,121 @@
import { createContext, useLayoutEffect, useState } from 'react';
import { themeCssVariables } from './themeCssVariables';
type StringLeaves<T> = {
[K in keyof T]: T[K] extends string ? string : StringLeaves<T[K]>;
};
type DeepMerge<T, U> = {
[K in keyof T]: K extends keyof U
? U[K] extends Record<string, unknown>
? T[K] extends Record<string, unknown>
? DeepMerge<T[K], U[K]>
: U[K]
: U[K]
: T[K];
};
// CSS variables that resolve to pure numbers at runtime
type NumericOverrides = {
icon: {
size: { sm: number; md: number; lg: number; xl: number };
stroke: { sm: number; md: number; lg: number };
};
animation: {
duration: { instant: number; fast: number; normal: number; slow: number };
};
text: {
lineHeight: { lg: number; md: number };
iconSizeMedium: number;
iconSizeSmall: number;
iconStrikeLight: number;
iconStrikeMedium: number;
iconStrikeBold: number;
};
spacingMultiplicator: number;
lastLayerZIndex: number;
};
export type ThemeType = DeepMerge<
StringLeaves<typeof themeCssVariables>,
NumericOverrides
>;
export type ThemeContextType = {
theme: ThemeType;
colorScheme: 'light' | 'dark';
};
const computeThemeFromCss = (): ThemeType => {
if (
typeof document === 'undefined' ||
typeof getComputedStyle !== 'function'
) {
return themeCssVariables as unknown as ThemeType;
}
const computedStyle = getComputedStyle(document.documentElement);
const resolve = (obj: Record<string, unknown>): Record<string, unknown> => {
const result: Record<string, unknown> = {};
for (const key of Object.keys(obj)) {
const value = obj[key];
if (typeof value === 'string' && value.startsWith('var(')) {
const varName = value.slice(4, -1);
const raw = computedStyle.getPropertyValue(varName).trim();
const num = Number(raw);
result[key] = raw !== '' && !isNaN(num) ? num : raw;
} else if (typeof value === 'object' && value !== null) {
result[key] = resolve(value as Record<string, unknown>);
} else {
result[key] = value;
}
}
return result;
};
return resolve(
themeCssVariables as unknown as Record<string, unknown>,
) as unknown as ThemeType;
};
const applyColorSchemeClass = (colorScheme: 'light' | 'dark') => {
if (typeof document === 'undefined') return;
const root = document.documentElement;
if (!root?.classList) return;
root.classList.toggle('dark', colorScheme === 'dark');
root.classList.toggle('light', colorScheme === 'light');
};
export const ThemeContext = createContext<ThemeContextType>({
theme: themeCssVariables as unknown as ThemeType,
colorScheme: 'light',
});
export const ThemeProvider = ({
children,
colorScheme,
}: {
children: React.ReactNode;
colorScheme: 'light' | 'dark';
}) => {
const [theme, setTheme] = useState<ThemeType>(() => {
applyColorSchemeClass(colorScheme);
return computeThemeFromCss();
});
useLayoutEffect(() => {
applyColorSchemeClass(colorScheme);
setTheme(computeThemeFromCss());
}, [colorScheme]);
return (
<ThemeContext.Provider value={{ theme, colorScheme }}>
{children}
</ThemeContext.Provider>
);
};
@@ -0,0 +1,28 @@
import {
MAIN_COLOR_NAMES,
type ThemeColor,
} from '@new-ui/theme/constants/MainColorNames';
import { getNextThemeColor } from '../getNextThemeColor';
describe('getNextThemeColor', () => {
it('returns the next theme color', () => {
const currentColor: ThemeColor = MAIN_COLOR_NAMES[0];
const nextColor: ThemeColor = MAIN_COLOR_NAMES[1];
expect(getNextThemeColor(MAIN_COLOR_NAMES, currentColor)).toBe(nextColor);
});
it('returns the first color when reaching the end', () => {
const currentColor: ThemeColor =
MAIN_COLOR_NAMES[MAIN_COLOR_NAMES.length - 1];
const nextColor: ThemeColor = MAIN_COLOR_NAMES[0];
expect(getNextThemeColor(MAIN_COLOR_NAMES, currentColor)).toBe(nextColor);
});
it('returns the first color when currentColorIsUndefined', () => {
const firstColor: ThemeColor = MAIN_COLOR_NAMES[0];
expect(getNextThemeColor(MAIN_COLOR_NAMES, undefined)).toBe(firstColor);
});
});
@@ -0,0 +1,50 @@
import { readFileSync } from 'fs';
import { resolve } from 'path';
import { themeCssVariables as newThemeCssVariables } from '../themeCssVariables';
import { themeCssVariables as oldThemeCssVariables } from '../../../../twenty-ui/src/theme-constants/themeCssVariables';
const NEW_DIR = resolve(__dirname, '..');
const OLD_DIR = resolve(__dirname, '../../../../twenty-ui/src/theme-constants');
// Parse a theme CSS file into ordered [name, value] pairs, normalizing
// whitespace so formatting differences (multi-line vs single-line values,
// header comment) don't affect the comparison. Stops each value at the ';'
// that terminates it, skipping ';' embedded in values (e.g. data URIs).
const parseTokens = (dir: string, file: string): [string, string][] => {
const text = readFileSync(resolve(dir, file), 'utf8');
const body = text.slice(text.indexOf('{') + 1, text.lastIndexOf('}'));
const re = /--t-([a-z0-9_-]+):\s*([\s\S]*?);(?=\s*(?:--t-|$))/g;
const tokens: [string, string][] = [];
let match: RegExpExecArray | null;
while ((match = re.exec(body)) !== null) {
const value = match[2]
.trim()
.replace(/\s+/g, ' ')
.replace(/\(\s+/g, '(')
.replace(/\s+\)/g, ')');
tokens.push([match[1], value]);
}
return tokens;
};
// twenty-new-ui must produce the exact same --t-* tokens and values (and order)
// as twenty-ui, so the swap is token-for-token with no visual change.
// Comparison ignores incidental formatting (header, whitespace) but not values.
describe('theme parity with twenty-ui', () => {
it('theme-light.css tokens match twenty-ui', () => {
expect(parseTokens(NEW_DIR, 'theme-light.css')).toEqual(
parseTokens(OLD_DIR, 'theme-light.css'),
);
});
it('theme-dark.css tokens match twenty-ui', () => {
expect(parseTokens(NEW_DIR, 'theme-dark.css')).toEqual(
parseTokens(OLD_DIR, 'theme-dark.css'),
);
});
it('themeCssVariables is deeply equal to twenty-ui', () => {
expect(newThemeCssVariables).toEqual(oldThemeCssVariables);
});
});
@@ -0,0 +1,3 @@
// CSS custom properties don't work in media queries, so MOBILE_VIEWPORT
// must be a static number rather than a var(--...) reference.
export const MOBILE_VIEWPORT = 768;
@@ -0,0 +1,15 @@
import { type ThemeColor } from '@new-ui/theme/constants/MainColorNames';
export const getNextThemeColor = (
colorNames: ThemeColor[],
currentColor?: ThemeColor,
): ThemeColor => {
if (currentColor === null || currentColor === undefined) {
return colorNames[0];
}
const currentColorIndex = colorNames.findIndex(
(color) => color === currentColor,
);
const nextColorIndex = (currentColorIndex + 1) % colorNames.length;
return colorNames[nextColorIndex];
};
@@ -0,0 +1,14 @@
/*
* _____ _
*|_ _|_ _____ _ __ | |_ _ _
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
* |_| \_/\_/ \___|_| |_|\__|\__, |
* |___/
*/
export { MOBILE_VIEWPORT } from './constants';
export { getNextThemeColor } from './getNextThemeColor';
export { themeCssVariables } from './themeCssVariables';
export type { ThemeType, ThemeContextType } from './ThemeProvider';
export { ThemeContext, ThemeProvider } from './ThemeProvider';
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
import * as RadixColors from '@radix-ui/colors';
import { COLOR_DARK } from '@new-ui/theme/constants/ColorsDark';
export const ACCENT_DARK = {
primary: COLOR_DARK.blue5,
secondary: COLOR_DARK.blue5,
tertiary: COLOR_DARK.blue3,
quaternary: COLOR_DARK.blue2,
accent3570: COLOR_DARK.blue8,
accent4060: COLOR_DARK.blue8,
accent1: RadixColors.indigoDarkP3.indigo1,
accent2: RadixColors.indigoDarkP3.indigo2,
accent3: RadixColors.indigoDarkP3.indigo3,
accent4: RadixColors.indigoDarkP3.indigo4,
accent5: RadixColors.indigoDarkP3.indigo5,
accent6: RadixColors.indigoDarkP3.indigo6,
accent7: RadixColors.indigoDarkP3.indigo7,
accent8: RadixColors.indigoDarkP3.indigo8,
accent9: RadixColors.indigoDarkP3.indigo9,
accent10: RadixColors.indigoDarkP3.indigo10,
accent11: RadixColors.indigoDarkP3.indigo11,
accent12: RadixColors.indigoDarkP3.indigo12,
};
@@ -0,0 +1,23 @@
import * as RadixColors from '@radix-ui/colors';
import { COLOR_LIGHT } from '@new-ui/theme/constants/ColorsLight';
export const ACCENT_LIGHT = {
primary: COLOR_LIGHT.blue5,
secondary: COLOR_LIGHT.blue5,
tertiary: COLOR_LIGHT.blue3,
quaternary: COLOR_LIGHT.blue2,
accent3570: COLOR_LIGHT.blue8,
accent4060: COLOR_LIGHT.blue8,
accent1: RadixColors.indigoP3.indigo1,
accent2: RadixColors.indigoP3.indigo2,
accent3: RadixColors.indigoP3.indigo3,
accent4: RadixColors.indigoP3.indigo4,
accent5: RadixColors.indigoP3.indigo5,
accent6: RadixColors.indigoP3.indigo6,
accent7: RadixColors.indigoP3.indigo7,
accent8: RadixColors.indigoP3.indigo8,
accent9: RadixColors.indigoP3.indigo9,
accent10: RadixColors.indigoP3.indigo10,
accent11: RadixColors.indigoP3.indigo11,
accent12: RadixColors.indigoP3.indigo12,
};
@@ -0,0 +1,10 @@
export const ANIMATION = {
duration: {
instant: 0.075,
fast: 0.15,
normal: 0.3,
slow: 1.5,
},
};
export type AnimationDuration = keyof typeof ANIMATION.duration;
@@ -0,0 +1,35 @@
import * as RadixColors from '@radix-ui/colors';
import { COLOR_DARK } from '@new-ui/theme/constants/ColorsDark';
import { GRAY_SCALE_DARK } from './GrayScaleDark';
import { TRANSPARENT_COLORS_DARK } from './TransparentColorsDark';
export const BACKGROUND_DARK = {
noisy: 'var(--t-background-noisy)',
primary: GRAY_SCALE_DARK.gray1,
secondary: GRAY_SCALE_DARK.gray2,
tertiary: GRAY_SCALE_DARK.gray4,
quaternary: GRAY_SCALE_DARK.gray5,
invertedPrimary: GRAY_SCALE_DARK.gray12,
invertedSecondary: GRAY_SCALE_DARK.gray11,
danger: COLOR_DARK.red3,
transparent: {
primary: RadixColors.blackP3A.blackA7,
secondary: RadixColors.blackP3A.blackA6,
strong: TRANSPARENT_COLORS_DARK.gray7,
medium: TRANSPARENT_COLORS_DARK.gray5,
light: TRANSPARENT_COLORS_DARK.gray2,
lighter: TRANSPARENT_COLORS_DARK.gray1,
danger: TRANSPARENT_COLORS_DARK.red3,
blue: TRANSPARENT_COLORS_DARK.blue4,
orange: TRANSPARENT_COLORS_DARK.orange4,
success: TRANSPARENT_COLORS_DARK.green4,
},
overlayPrimary: '#000000b8',
overlaySecondary: '#0000005c',
overlayTertiary: '#0000005c',
radialGradient: `radial-gradient(50% 62.62% at 50% 0%, ${GRAY_SCALE_DARK.gray9} 0%, ${GRAY_SCALE_DARK.gray10} 100%)`,
radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, ${GRAY_SCALE_DARK.gray10} 0%, ${GRAY_SCALE_DARK.gray11} 100%)`,
primaryInverted: GRAY_SCALE_DARK.gray12,
primaryInvertedHover: GRAY_SCALE_DARK.gray11,
};
@@ -0,0 +1,35 @@
import * as RadixColors from '@radix-ui/colors';
import { COLOR_LIGHT } from '@new-ui/theme/constants/ColorsLight';
import { GRAY_SCALE_LIGHT } from './GrayScaleLight';
import { TRANSPARENT_COLORS_LIGHT } from './TransparentColorsLight';
export const BACKGROUND_LIGHT = {
noisy: 'var(--t-background-noisy)',
primary: GRAY_SCALE_LIGHT.gray1,
secondary: GRAY_SCALE_LIGHT.gray2,
tertiary: GRAY_SCALE_LIGHT.gray4,
quaternary: GRAY_SCALE_LIGHT.gray5,
invertedPrimary: GRAY_SCALE_LIGHT.gray12,
invertedSecondary: GRAY_SCALE_LIGHT.gray11,
danger: COLOR_LIGHT.red3,
transparent: {
primary: RadixColors.whiteP3A.whiteA7,
secondary: RadixColors.whiteP3A.whiteA6,
strong: TRANSPARENT_COLORS_LIGHT.gray7,
medium: TRANSPARENT_COLORS_LIGHT.gray5,
light: TRANSPARENT_COLORS_LIGHT.gray2,
lighter: TRANSPARENT_COLORS_LIGHT.gray1,
danger: TRANSPARENT_COLORS_LIGHT.red3,
blue: TRANSPARENT_COLORS_LIGHT.blue3,
orange: TRANSPARENT_COLORS_LIGHT.orange3,
success: TRANSPARENT_COLORS_LIGHT.green3,
},
overlayPrimary: TRANSPARENT_COLORS_LIGHT.gray11,
overlaySecondary: TRANSPARENT_COLORS_LIGHT.gray9,
overlayTertiary: TRANSPARENT_COLORS_LIGHT.gray4,
radialGradient: `radial-gradient(50% 62.62% at 50% 0%, ${GRAY_SCALE_LIGHT.gray9} 0%, ${GRAY_SCALE_LIGHT.gray10} 100%)`,
radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, ${GRAY_SCALE_LIGHT.gray10} 0%, ${GRAY_SCALE_LIGHT.gray11} 100%)`,
primaryInverted: GRAY_SCALE_LIGHT.gray12,
primaryInvertedHover: GRAY_SCALE_LIGHT.gray11,
};
@@ -0,0 +1,5 @@
export const BLUR_DARK = {
light: 'blur(6px) saturate(200%) contrast(100%) brightness(130%)',
medium: 'blur(12px) saturate(200%) contrast(100%) brightness(130%)',
strong: 'blur(20px) saturate(200%) contrast(100%) brightness(130%)',
};
@@ -0,0 +1,5 @@
export const BLUR_LIGHT = {
light: 'blur(6px) saturate(200%) contrast(50%) brightness(130%)',
medium: 'blur(12px) saturate(200%) contrast(50%) brightness(130%)',
strong: 'blur(20px) saturate(200%) contrast(50%) brightness(130%)',
};
@@ -0,0 +1,11 @@
export const BORDER_COMMON = {
radius: {
xs: '2px',
sm: '4px',
md: '8px',
xl: '20px',
xxl: '40px',
pill: '999px',
rounded: '100%',
},
};
@@ -0,0 +1,18 @@
import { COLOR_DARK } from '@new-ui/theme/constants/ColorsDark';
import { BORDER_COMMON } from './BorderCommon';
import { GRAY_SCALE_DARK } from './GrayScaleDark';
import { TRANSPARENT_COLORS_DARK } from './TransparentColorsDark';
export const BORDER_DARK = {
color: {
strong: GRAY_SCALE_DARK.gray6,
medium: GRAY_SCALE_DARK.gray5,
light: GRAY_SCALE_DARK.gray4,
secondaryInverted: GRAY_SCALE_DARK.gray11,
inverted: GRAY_SCALE_DARK.gray12,
danger: COLOR_DARK.red5,
blue: COLOR_DARK.blue7,
transparentStrong: TRANSPARENT_COLORS_DARK.gray4,
},
...BORDER_COMMON,
};
@@ -0,0 +1,18 @@
import { COLOR_LIGHT } from '@new-ui/theme/constants/ColorsLight';
import { BORDER_COMMON } from './BorderCommon';
import { GRAY_SCALE_LIGHT } from './GrayScaleLight';
import { TRANSPARENT_COLORS_LIGHT } from './TransparentColorsLight';
export const BORDER_LIGHT = {
color: {
strong: GRAY_SCALE_LIGHT.gray6,
medium: GRAY_SCALE_LIGHT.gray5,
light: GRAY_SCALE_LIGHT.gray4,
secondaryInverted: GRAY_SCALE_LIGHT.gray11,
inverted: GRAY_SCALE_LIGHT.gray12,
danger: COLOR_LIGHT.red5,
blue: COLOR_LIGHT.blue7,
transparentStrong: TRANSPARENT_COLORS_LIGHT.gray4,
},
...BORDER_COMMON,
};
@@ -0,0 +1,19 @@
import { RGBA } from '@new-ui/theme/constants/Rgba';
/* oxlint-disable twenty/no-hardcoded-colors */
export const BOX_SHADOW_DARK = {
color: RGBA('#000000', 0.6),
light: `0px 2px 4px 0px ${RGBA(
'#000000',
0.04,
)}, 0px 0px 4px 0px ${RGBA('#000000', 0.08)}`,
strong: `2px 4px 16px 0px ${RGBA(
'#000000',
0.16,
)}, 0px 2px 4px 0px ${RGBA('#000000', 0.08)}`,
underline: `0px 1px 0px 0px ${RGBA('#000000', 0.32)}`,
superHeavy: `2px 4px 16px 0px ${RGBA(
'#000000',
0.12,
)}, 0px 2px 4px 0px ${RGBA('#000000', 0.04)}`,
};
@@ -0,0 +1,9 @@
import { GRAY_SCALE_LIGHT_ALPHA } from '@new-ui/theme/constants/GrayScaleLightAlpha';
export const BOX_SHADOW_LIGHT = {
color: GRAY_SCALE_LIGHT_ALPHA.gray2,
light: `0px 2px 4px 0px ${GRAY_SCALE_LIGHT_ALPHA.gray2}, 0px 0px 4px 0px ${GRAY_SCALE_LIGHT_ALPHA.gray5}`,
strong: `2px 4px 16px 0px ${GRAY_SCALE_LIGHT_ALPHA.gray7}, 0px 2px 4px 0px ${GRAY_SCALE_LIGHT_ALPHA.gray5}`,
underline: `0px 1px 0px 0px ${GRAY_SCALE_LIGHT_ALPHA.gray9}`,
superHeavy: `0px 0px 8px 0px ${GRAY_SCALE_LIGHT_ALPHA.gray7}, 0px 8px 64px -16px ${GRAY_SCALE_LIGHT_ALPHA.gray10}, 0px 24px 56px -16px ${GRAY_SCALE_LIGHT_ALPHA.gray5}`,
};
@@ -0,0 +1,12 @@
import { COLOR_DARK } from '@new-ui/theme/constants/ColorsDark';
export const CODE_DARK = {
text: {
gray: COLOR_DARK.gray10,
sky: COLOR_DARK.sky10,
pink: COLOR_DARK.pink10,
orange: COLOR_DARK.orange8,
green: COLOR_DARK.lime8,
},
font: { family: 'DM Mono' },
};
@@ -0,0 +1,12 @@
import { COLOR_LIGHT } from '@new-ui/theme/constants/ColorsLight';
export const CODE_LIGHT = {
text: {
gray: COLOR_LIGHT.gray10,
sky: COLOR_LIGHT.sky10,
pink: COLOR_LIGHT.pink10,
orange: COLOR_LIGHT.orange8,
green: COLOR_LIGHT.lime8,
},
font: { family: 'DM Mono' },
};
@@ -0,0 +1,9 @@
import { MAIN_COLORS_DARK } from './MainColorsDark';
import { SECONDARY_COLORS_DARK } from './SecondaryColorsDark';
import { TRANSPARENT_COLORS_DARK } from './TransparentColorsDark';
export const COLOR_DARK = {
...MAIN_COLORS_DARK,
...SECONDARY_COLORS_DARK,
transparent: TRANSPARENT_COLORS_DARK,
};
@@ -0,0 +1,9 @@
import { MAIN_COLORS_LIGHT } from './MainColorsLight';
import { SECONDARY_COLORS_LIGHT } from './SecondaryColorsLight';
import { TRANSPARENT_COLORS_LIGHT } from './TransparentColorsLight';
export const COLOR_LIGHT = {
...MAIN_COLORS_LIGHT,
...SECONDARY_COLORS_LIGHT,
transparent: TRANSPARENT_COLORS_LIGHT,
};
@@ -0,0 +1,3 @@
import { type ThemeColor } from './MainColorNames';
export const DEFAULT_THEME_COLOR_FALLBACK: ThemeColor = 'gray';
@@ -0,0 +1,17 @@
export const FONT_COMMON = {
size: {
xxs: '0.625rem',
xs: '0.85rem',
sm: '0.92rem',
md: '1rem',
lg: '1.23rem',
xl: '1.54rem',
xxl: '1.85rem',
},
weight: {
regular: 400,
medium: 500,
semiBold: 600,
},
family: 'Inter, sans-serif',
};
@@ -0,0 +1,16 @@
import { COLOR_DARK } from '@new-ui/theme/constants/ColorsDark';
import { FONT_COMMON } from './FontCommon';
import { GRAY_SCALE_DARK } from './GrayScaleDark';
export const FONT_DARK = {
color: {
primary: GRAY_SCALE_DARK.gray12,
secondary: GRAY_SCALE_DARK.gray11,
tertiary: GRAY_SCALE_DARK.gray9,
light: GRAY_SCALE_DARK.gray8,
extraLight: GRAY_SCALE_DARK.gray7,
inverted: GRAY_SCALE_DARK.gray1,
danger: COLOR_DARK.red,
},
...FONT_COMMON,
};
@@ -0,0 +1,16 @@
import { COLOR_LIGHT } from '@new-ui/theme/constants/ColorsLight';
import { FONT_COMMON } from './FontCommon';
import { GRAY_SCALE_LIGHT } from './GrayScaleLight';
export const FONT_LIGHT = {
color: {
primary: GRAY_SCALE_LIGHT.gray12,
secondary: GRAY_SCALE_LIGHT.gray11,
tertiary: GRAY_SCALE_LIGHT.gray9,
light: GRAY_SCALE_LIGHT.gray8,
extraLight: GRAY_SCALE_LIGHT.gray7,
inverted: GRAY_SCALE_LIGHT.gray1,
danger: COLOR_LIGHT.red,
},
...FONT_COMMON,
};
@@ -0,0 +1,14 @@
export const GRAY_SCALE_DARK = {
gray1: 'color(display-p3 0.09 0.09 0.09)',
gray2: 'color(display-p3 0.106 0.106 0.106)',
gray3: 'color(display-p3 0.098 0.098 0.098)',
gray4: 'color(display-p3 0.114 0.114 0.114)',
gray5: 'color(display-p3 0.133 0.133 0.133)',
gray6: 'color(display-p3 0.282 0.282 0.282)',
gray7: 'color(display-p3 0.298 0.298 0.298)',
gray8: 'color(display-p3 0.4 0.4 0.4)',
gray9: 'color(display-p3 0.506 0.506 0.506)',
gray10: 'color(display-p3 0.482 0.482 0.482)',
gray11: 'color(display-p3 0.702 0.702 0.702)',
gray12: 'color(display-p3 0.922 0.922 0.922)',
};
@@ -0,0 +1,14 @@
export const GRAY_SCALE_DARK_ALPHA = {
gray1: 'color(display-p3 1 1 1 / 0.031)',
gray2: 'color(display-p3 1 1 1 / 0.059)',
gray3: 'color(display-p3 1 1 1 / 0.047)',
gray4: 'color(display-p3 1 1 1 / 0.071)',
gray5: 'color(display-p3 1 1 1 / 0.102)',
gray6: 'color(display-p3 1 1 1 / 0.114)',
gray7: 'color(display-p3 1 1 1 / 0.141)',
gray8: 'color(display-p3 1 1 1 / 0.22)',
gray9: 'color(display-p3 1 1 1 / 0.427)',
gray10: 'color(display-p3 1 1 1 / 0.478)',
gray11: 'color(display-p3 1 1 1 / 0.565)',
gray12: 'color(display-p3 1 1 1 / 0.91)',
};
@@ -0,0 +1,14 @@
export const GRAY_SCALE_LIGHT = {
gray1: 'color(display-p3 1 1 1)',
gray2: 'color(display-p3 0.988 0.988 0.988)',
gray3: 'color(display-p3 0.976 0.976 0.976)',
gray4: 'color(display-p3 0.945 0.945 0.945)',
gray5: 'color(display-p3 0.922 0.922 0.922)',
gray6: 'color(display-p3 0.839 0.839 0.839)',
gray7: 'color(display-p3 0.8 0.8 0.8)',
gray8: 'color(display-p3 0.702 0.702 0.702)',
gray9: 'color(display-p3 0.6 0.6 0.6)',
gray10: 'color(display-p3 0.514 0.514 0.514)',
gray11: 'color(display-p3 0.4 0.4 0.4)',
gray12: 'color(display-p3 0.2 0.2 0.2)',
};
@@ -0,0 +1,14 @@
export const GRAY_SCALE_LIGHT_ALPHA = {
gray1: 'color(display-p3 0 0 0 / 0.02)',
gray2: 'color(display-p3 0 0 0 / 0.039)',
gray3: 'color(display-p3 0 0 0 / 0.047)',
gray4: 'color(display-p3 0 0 0 / 0.071)',
gray5: 'color(display-p3 0 0 0 / 0.078)',
gray6: 'color(display-p3 0 0 0 / 0.114)',
gray7: 'color(display-p3 0 0 0 / 0.161)',
gray8: 'color(display-p3 0 0 0 / 0.22)',
gray9: 'color(display-p3 0 0 0 / 0.361)',
gray10: 'color(display-p3 0 0 0 / 0.478)',
gray11: 'color(display-p3 0 0 0 / 0.722)',
gray12: 'color(display-p3 0 0 0 / 0.91)',
};
@@ -0,0 +1,13 @@
export const ICON = {
size: {
sm: 14,
md: 16,
lg: 20,
xl: 24,
},
stroke: {
sm: 1.6,
md: 2,
lg: 2.5,
},
};
@@ -0,0 +1,13 @@
import { COLOR_DARK } from '@new-ui/theme/constants/ColorsDark';
import { GRAY_SCALE_DARK } from '@new-ui/theme/constants/GrayScaleDark';
export const ILLUSTRATION_ICON_DARK = {
color: {
blue: COLOR_DARK.blue10,
gray: GRAY_SCALE_DARK.gray8,
},
fill: {
blue: COLOR_DARK.blue12,
gray: GRAY_SCALE_DARK.gray5,
},
};
@@ -0,0 +1,13 @@
import { COLOR_LIGHT } from '@new-ui/theme/constants/ColorsLight';
import { GRAY_SCALE_LIGHT } from '@new-ui/theme/constants/GrayScaleLight';
export const ILLUSTRATION_ICON_LIGHT = {
color: {
blue: COLOR_LIGHT.blue8,
gray: GRAY_SCALE_LIGHT.gray9,
},
fill: {
blue: COLOR_LIGHT.blue5,
gray: GRAY_SCALE_LIGHT.gray5,
},
};
@@ -0,0 +1,5 @@
import { MAIN_COLORS_LIGHT } from './MainColorsLight';
export const MAIN_COLOR_NAMES = Object.keys(MAIN_COLORS_LIGHT) as ThemeColor[];
export type ThemeColor = keyof typeof MAIN_COLORS_LIGHT;
@@ -0,0 +1,41 @@
import * as RadixColors from '@radix-ui/colors';
import { GRAY_SCALE_DARK } from '@new-ui/theme/constants/GrayScaleDark';
export const MAIN_COLORS_DARK = {
// Reds
red: RadixColors.redDarkP3.red9,
ruby: RadixColors.rubyDarkP3.ruby9,
crimson: RadixColors.crimsonDarkP3.crimson9,
tomato: RadixColors.tomatoDarkP3.tomato9,
// Oranges & Yellows
orange: RadixColors.orangeDarkP3.orange9,
amber: RadixColors.amberDarkP3.amber9,
yellow: RadixColors.yellowDarkP3.yellow9,
// Greens
lime: RadixColors.limeDarkP3.lime9,
grass: RadixColors.grassDarkP3.grass9,
green: RadixColors.greenDarkP3.green9,
jade: RadixColors.jadeDarkP3.jade9,
mint: RadixColors.mintDarkP3.mint9,
// Cyans & Blues
turquoise: RadixColors.tealDarkP3.teal9,
cyan: RadixColors.cyanDarkP3.cyan9,
sky: RadixColors.skyDarkP3.sky9,
blue: RadixColors.indigoDarkP3.indigo9,
// Purples & Pinks
iris: RadixColors.irisDarkP3.iris9,
violet: RadixColors.violetDarkP3.violet9,
purple: RadixColors.purpleDarkP3.purple9,
plum: RadixColors.plumDarkP3.plum9,
pink: RadixColors.pinkDarkP3.pink9,
// Earth tones & Neutrals
bronze: RadixColors.bronzeDarkP3.bronze9,
gold: RadixColors.goldDarkP3.gold9,
brown: RadixColors.brownDarkP3.brown9,
gray: GRAY_SCALE_DARK.gray7,
};
@@ -0,0 +1,41 @@
import * as RadixColors from '@radix-ui/colors';
import { GRAY_SCALE_LIGHT } from './GrayScaleLight';
export const MAIN_COLORS_LIGHT = {
// Reds
red: RadixColors.redP3.red9,
ruby: RadixColors.rubyP3.ruby9,
crimson: RadixColors.crimsonP3.crimson9,
tomato: RadixColors.tomatoP3.tomato9,
// Oranges & Yellows
orange: RadixColors.orangeP3.orange9,
amber: RadixColors.amberP3.amber9,
yellow: RadixColors.yellowP3.yellow9,
// Greens
lime: RadixColors.limeP3.lime9,
grass: RadixColors.grassP3.grass9,
green: RadixColors.greenP3.green9,
jade: RadixColors.jadeP3.jade9,
mint: RadixColors.mintP3.mint9,
// Cyans & Blues
turquoise: RadixColors.tealP3.teal9,
cyan: RadixColors.cyanP3.cyan9,
sky: RadixColors.skyP3.sky9,
blue: RadixColors.indigoP3.indigo9,
// Purples & Pinks
iris: RadixColors.irisP3.iris9,
violet: RadixColors.violetP3.violet9,
purple: RadixColors.purpleP3.purple9,
plum: RadixColors.plumP3.plum9,
pink: RadixColors.pinkP3.pink9,
// Earth tones & Neutrals
bronze: RadixColors.bronzeP3.bronze9,
gold: RadixColors.goldP3.gold9,
brown: RadixColors.brownP3.brown9,
gray: GRAY_SCALE_LIGHT.gray9,
};
@@ -0,0 +1,23 @@
export const MODAL: {
size: { [key: string]: { width?: string; height?: string } };
} = {
size: {
sm: {
width: '300px',
},
md: {
width: '400px',
},
lg: {
width: '53%',
},
xl: {
width: '1200px',
height: '800px',
},
fullscreen: {
width: '100dvw',
height: '100dvh',
},
},
};
@@ -0,0 +1,8 @@
/* oxlint-disable twenty/no-hardcoded-colors */
import hexRgb from 'hex-rgb';
export const RGBA = (hex: string, alpha: number) => {
return `rgba(${hexRgb(hex, { format: 'array' })
.slice(0, -1)
.join(',')},${alpha})`;
};
@@ -0,0 +1,394 @@
import * as RadixColors from '@radix-ui/colors';
import { GRAY_SCALE_DARK } from '@new-ui/theme/constants/GrayScaleDark';
export const SECONDARY_COLORS_DARK = {
yellow1: RadixColors.yellowDarkP3.yellow1,
yellow2: RadixColors.yellowDarkP3.yellow2,
yellow3: RadixColors.yellowDarkP3.yellow3,
yellow4: RadixColors.yellowDarkP3.yellow4,
yellow5: RadixColors.yellowDarkP3.yellow5,
yellow6: RadixColors.yellowDarkP3.yellow6,
yellow7: RadixColors.yellowDarkP3.yellow7,
yellow8: RadixColors.yellowDarkP3.yellow8,
yellow9: RadixColors.yellowDarkP3.yellow9,
yellow10: RadixColors.yellowDarkP3.yellow10,
yellow11: RadixColors.yellowDarkP3.yellow11,
yellow12: RadixColors.yellowDarkP3.yellow12,
green1: RadixColors.greenDarkP3.green1,
green2: RadixColors.greenDarkP3.green2,
green3: RadixColors.greenDarkP3.green3,
green4: RadixColors.greenDarkP3.green4,
green5: RadixColors.greenDarkP3.green5,
green6: RadixColors.greenDarkP3.green6,
green7: RadixColors.greenDarkP3.green7,
green8: RadixColors.greenDarkP3.green8,
green9: RadixColors.greenDarkP3.green9,
green10: RadixColors.greenDarkP3.green10,
green11: RadixColors.greenDarkP3.green11,
green12: RadixColors.greenDarkP3.green12,
turquoise1: RadixColors.tealDarkP3.teal1,
turquoise2: RadixColors.tealDarkP3.teal2,
turquoise3: RadixColors.tealDarkP3.teal3,
turquoise4: RadixColors.tealDarkP3.teal4,
turquoise5: RadixColors.tealDarkP3.teal5,
turquoise6: RadixColors.tealDarkP3.teal6,
turquoise7: RadixColors.tealDarkP3.teal7,
turquoise8: RadixColors.tealDarkP3.teal8,
turquoise9: RadixColors.tealDarkP3.teal9,
turquoise10: RadixColors.tealDarkP3.teal10,
turquoise11: RadixColors.tealDarkP3.teal11,
turquoise12: RadixColors.tealDarkP3.teal12,
sky1: RadixColors.skyDarkP3.sky1,
sky2: RadixColors.skyDarkP3.sky2,
sky3: RadixColors.skyDarkP3.sky3,
sky4: RadixColors.skyDarkP3.sky4,
sky5: RadixColors.skyDarkP3.sky5,
sky6: RadixColors.skyDarkP3.sky6,
sky7: RadixColors.skyDarkP3.sky7,
sky8: RadixColors.skyDarkP3.sky8,
sky9: RadixColors.skyP3.sky9,
sky10: RadixColors.skyDarkP3.sky10,
sky11: RadixColors.skyDarkP3.sky11,
sky12: RadixColors.skyDarkP3.sky12,
blue1: RadixColors.indigoDarkP3.indigo1,
blue2: RadixColors.indigoDarkP3.indigo2,
blue3: RadixColors.indigoDarkP3.indigo3,
blue4: RadixColors.indigoDarkP3.indigo4,
blue5: RadixColors.indigoDarkP3.indigo5,
blue6: RadixColors.indigoDarkP3.indigo6,
blue7: RadixColors.indigoDarkP3.indigo7,
blue8: RadixColors.indigoDarkP3.indigo8,
blue9: RadixColors.indigoDarkP3.indigo9,
blue10: RadixColors.indigoDarkP3.indigo10,
blue11: RadixColors.indigoDarkP3.indigo11,
blue12: RadixColors.indigoDarkP3.indigo12,
purple1: RadixColors.purpleDarkP3.purple1,
purple2: RadixColors.purpleDarkP3.purple2,
purple3: RadixColors.purpleDarkP3.purple3,
purple4: RadixColors.purpleDarkP3.purple4,
purple5: RadixColors.purpleDarkP3.purple5,
purple6: RadixColors.purpleDarkP3.purple6,
purple7: RadixColors.purpleDarkP3.purple7,
purple8: RadixColors.purpleDarkP3.purple8,
purple9: RadixColors.purpleDarkP3.purple9,
purple10: RadixColors.purpleDarkP3.purple10,
purple11: RadixColors.purpleDarkP3.purple11,
purple12: RadixColors.purpleDarkP3.purple12,
pink1: RadixColors.pinkDarkP3.pink1,
pink2: RadixColors.pinkDarkP3.pink2,
pink3: RadixColors.pinkDarkP3.pink3,
pink4: RadixColors.pinkDarkP3.pink4,
pink5: RadixColors.pinkDarkP3.pink5,
pink6: RadixColors.pinkDarkP3.pink6,
pink7: RadixColors.pinkDarkP3.pink7,
pink8: RadixColors.pinkDarkP3.pink8,
pink9: RadixColors.pinkDarkP3.pink9,
pink10: RadixColors.pinkDarkP3.pink10,
pink11: RadixColors.pinkDarkP3.pink11,
pink12: RadixColors.pinkDarkP3.pink12,
red1: RadixColors.redDarkP3.red1,
red2: RadixColors.redDarkP3.red2,
red3: RadixColors.redDarkP3.red3,
red4: RadixColors.redDarkP3.red4,
red5: RadixColors.redDarkP3.red5,
red6: RadixColors.redDarkP3.red6,
red7: RadixColors.redDarkP3.red7,
red8: RadixColors.redDarkP3.red8,
red9: RadixColors.redDarkP3.red9,
red10: RadixColors.redDarkP3.red10,
red11: RadixColors.redDarkP3.red11,
red12: RadixColors.redDarkP3.red12,
orange1: RadixColors.orangeDarkP3.orange1,
orange2: RadixColors.orangeDarkP3.orange2,
orange3: RadixColors.orangeDarkP3.orange3,
orange4: RadixColors.orangeDarkP3.orange4,
orange5: RadixColors.orangeDarkP3.orange5,
orange6: RadixColors.orangeDarkP3.orange6,
orange7: RadixColors.orangeDarkP3.orange7,
orange8: RadixColors.orangeDarkP3.orange8,
orange9: RadixColors.orangeDarkP3.orange9,
orange10: RadixColors.orangeDarkP3.orange10,
orange11: RadixColors.orangeDarkP3.orange11,
orange12: RadixColors.orangeDarkP3.orange12,
gray1: GRAY_SCALE_DARK.gray1,
gray2: GRAY_SCALE_DARK.gray2,
gray3: GRAY_SCALE_DARK.gray3,
gray4: GRAY_SCALE_DARK.gray4,
gray5: GRAY_SCALE_DARK.gray5,
gray6: GRAY_SCALE_DARK.gray6,
gray7: GRAY_SCALE_DARK.gray7,
gray8: GRAY_SCALE_DARK.gray8,
gray9: GRAY_SCALE_DARK.gray9,
gray10: GRAY_SCALE_DARK.gray10,
gray11: GRAY_SCALE_DARK.gray11,
gray12: GRAY_SCALE_DARK.gray12,
mauve1: RadixColors.mauveDarkP3.mauve1,
mauve2: RadixColors.mauveDarkP3.mauve2,
mauve3: RadixColors.mauveDarkP3.mauve3,
mauve4: RadixColors.mauveDarkP3.mauve4,
mauve5: RadixColors.mauveDarkP3.mauve5,
mauve6: RadixColors.mauveDarkP3.mauve6,
mauve7: RadixColors.mauveDarkP3.mauve7,
mauve8: RadixColors.mauveDarkP3.mauve8,
mauve9: RadixColors.mauveDarkP3.mauve9,
mauve10: RadixColors.mauveDarkP3.mauve10,
mauve11: RadixColors.mauveDarkP3.mauve11,
mauve12: RadixColors.mauveDarkP3.mauve12,
slate1: RadixColors.slateDarkP3.slate1,
slate2: RadixColors.slateDarkP3.slate2,
slate3: RadixColors.slateDarkP3.slate3,
slate4: RadixColors.slateDarkP3.slate4,
slate5: RadixColors.slateDarkP3.slate5,
slate6: RadixColors.slateDarkP3.slate6,
slate7: RadixColors.slateDarkP3.slate7,
slate8: RadixColors.slateDarkP3.slate8,
slate9: RadixColors.slateDarkP3.slate9,
slate10: RadixColors.slateDarkP3.slate10,
slate11: RadixColors.slateDarkP3.slate11,
slate12: RadixColors.slateDarkP3.slate12,
sage1: RadixColors.sageDarkP3.sage1,
sage2: RadixColors.sageDarkP3.sage2,
sage3: RadixColors.sageDarkP3.sage3,
sage4: RadixColors.sageDarkP3.sage4,
sage5: RadixColors.sageDarkP3.sage5,
sage6: RadixColors.sageDarkP3.sage6,
sage7: RadixColors.sageDarkP3.sage7,
sage8: RadixColors.sageDarkP3.sage8,
sage9: RadixColors.sageDarkP3.sage9,
sage10: RadixColors.sageDarkP3.sage10,
sage11: RadixColors.sageDarkP3.sage11,
sage12: RadixColors.sageDarkP3.sage12,
olive1: RadixColors.oliveDarkP3.olive1,
olive2: RadixColors.oliveDarkP3.olive2,
olive3: RadixColors.oliveDarkP3.olive3,
olive4: RadixColors.oliveDarkP3.olive4,
olive5: RadixColors.oliveDarkP3.olive5,
olive6: RadixColors.oliveDarkP3.olive6,
olive7: RadixColors.oliveDarkP3.olive7,
olive8: RadixColors.oliveDarkP3.olive8,
olive9: RadixColors.oliveDarkP3.olive9,
olive10: RadixColors.oliveDarkP3.olive10,
olive11: RadixColors.oliveDarkP3.olive11,
olive12: RadixColors.oliveDarkP3.olive12,
sand1: RadixColors.sandDarkP3.sand1,
sand2: RadixColors.sandDarkP3.sand2,
sand3: RadixColors.sandDarkP3.sand3,
sand4: RadixColors.sandDarkP3.sand4,
sand5: RadixColors.sandDarkP3.sand5,
sand6: RadixColors.sandDarkP3.sand6,
sand7: RadixColors.sandDarkP3.sand7,
sand8: RadixColors.sandDarkP3.sand8,
sand9: RadixColors.sandDarkP3.sand9,
sand10: RadixColors.sandDarkP3.sand10,
sand11: RadixColors.sandDarkP3.sand11,
sand12: RadixColors.sandDarkP3.sand12,
tomato1: RadixColors.tomatoDarkP3.tomato1,
tomato2: RadixColors.tomatoDarkP3.tomato2,
tomato3: RadixColors.tomatoDarkP3.tomato3,
tomato4: RadixColors.tomatoDarkP3.tomato4,
tomato5: RadixColors.tomatoDarkP3.tomato5,
tomato6: RadixColors.tomatoDarkP3.tomato6,
tomato7: RadixColors.tomatoDarkP3.tomato7,
tomato8: RadixColors.tomatoDarkP3.tomato8,
tomato9: RadixColors.tomatoDarkP3.tomato9,
tomato10: RadixColors.tomatoDarkP3.tomato10,
tomato11: RadixColors.tomatoDarkP3.tomato11,
tomato12: RadixColors.tomatoDarkP3.tomato12,
ruby1: RadixColors.rubyDarkP3.ruby1,
ruby2: RadixColors.rubyDarkP3.ruby2,
ruby3: RadixColors.rubyDarkP3.ruby3,
ruby4: RadixColors.rubyDarkP3.ruby4,
ruby5: RadixColors.rubyDarkP3.ruby5,
ruby6: RadixColors.rubyDarkP3.ruby6,
ruby7: RadixColors.rubyDarkP3.ruby7,
ruby8: RadixColors.rubyDarkP3.ruby8,
ruby9: RadixColors.rubyDarkP3.ruby9,
ruby10: RadixColors.rubyDarkP3.ruby10,
ruby11: RadixColors.rubyDarkP3.ruby11,
ruby12: RadixColors.rubyDarkP3.ruby12,
crimson1: RadixColors.crimsonDarkP3.crimson1,
crimson2: RadixColors.crimsonDarkP3.crimson2,
crimson3: RadixColors.crimsonDarkP3.crimson3,
crimson4: RadixColors.crimsonDarkP3.crimson4,
crimson5: RadixColors.crimsonDarkP3.crimson5,
crimson6: RadixColors.crimsonDarkP3.crimson6,
crimson7: RadixColors.crimsonDarkP3.crimson7,
crimson8: RadixColors.crimsonDarkP3.crimson8,
crimson9: RadixColors.crimsonDarkP3.crimson9,
crimson10: RadixColors.crimsonDarkP3.crimson10,
crimson11: RadixColors.crimsonDarkP3.crimson11,
crimson12: RadixColors.crimsonDarkP3.crimson12,
plum1: RadixColors.plumDarkP3.plum1,
plum2: RadixColors.plumDarkP3.plum2,
plum3: RadixColors.plumDarkP3.plum3,
plum4: RadixColors.plumDarkP3.plum4,
plum5: RadixColors.plumDarkP3.plum5,
plum6: RadixColors.plumDarkP3.plum6,
plum7: RadixColors.plumDarkP3.plum7,
plum8: RadixColors.plumDarkP3.plum8,
plum9: RadixColors.plumDarkP3.plum9,
plum10: RadixColors.plumDarkP3.plum10,
plum11: RadixColors.plumDarkP3.plum11,
plum12: RadixColors.plumDarkP3.plum12,
violet1: RadixColors.violetDarkP3.violet1,
violet2: RadixColors.violetDarkP3.violet2,
violet3: RadixColors.violetDarkP3.violet3,
violet4: RadixColors.violetDarkP3.violet4,
violet5: RadixColors.violetDarkP3.violet5,
violet6: RadixColors.violetDarkP3.violet6,
violet7: RadixColors.violetDarkP3.violet7,
violet8: RadixColors.violetDarkP3.violet8,
violet9: RadixColors.violetDarkP3.violet9,
violet10: RadixColors.violetDarkP3.violet10,
violet11: RadixColors.violetDarkP3.violet11,
violet12: RadixColors.violetDarkP3.violet12,
iris1: RadixColors.irisDarkP3.iris1,
iris2: RadixColors.irisDarkP3.iris2,
iris3: RadixColors.irisDarkP3.iris3,
iris4: RadixColors.irisDarkP3.iris4,
iris5: RadixColors.irisDarkP3.iris5,
iris6: RadixColors.irisDarkP3.iris6,
iris7: RadixColors.irisDarkP3.iris7,
iris8: RadixColors.irisDarkP3.iris8,
iris9: RadixColors.irisDarkP3.iris9,
iris10: RadixColors.irisDarkP3.iris10,
iris11: RadixColors.irisDarkP3.iris11,
iris12: RadixColors.irisDarkP3.iris12,
cyan1: RadixColors.cyanDarkP3.cyan1,
cyan2: RadixColors.cyanDarkP3.cyan2,
cyan3: RadixColors.cyanDarkP3.cyan3,
cyan4: RadixColors.cyanDarkP3.cyan4,
cyan5: RadixColors.cyanDarkP3.cyan5,
cyan6: RadixColors.cyanDarkP3.cyan6,
cyan7: RadixColors.cyanDarkP3.cyan7,
cyan8: RadixColors.cyanDarkP3.cyan8,
cyan9: RadixColors.cyanDarkP3.cyan9,
cyan10: RadixColors.cyanDarkP3.cyan10,
cyan11: RadixColors.cyanDarkP3.cyan11,
cyan12: RadixColors.cyanDarkP3.cyan12,
jade1: RadixColors.jadeDarkP3.jade1,
jade2: RadixColors.jadeDarkP3.jade2,
jade3: RadixColors.jadeDarkP3.jade3,
jade4: RadixColors.jadeDarkP3.jade4,
jade5: RadixColors.jadeDarkP3.jade5,
jade6: RadixColors.jadeDarkP3.jade6,
jade7: RadixColors.jadeDarkP3.jade7,
jade8: RadixColors.jadeDarkP3.jade8,
jade9: RadixColors.jadeDarkP3.jade9,
jade10: RadixColors.jadeDarkP3.jade10,
jade11: RadixColors.jadeDarkP3.jade11,
jade12: RadixColors.jadeDarkP3.jade12,
grass1: RadixColors.grassDarkP3.grass1,
grass2: RadixColors.grassDarkP3.grass2,
grass3: RadixColors.grassDarkP3.grass3,
grass4: RadixColors.grassDarkP3.grass4,
grass5: RadixColors.grassDarkP3.grass5,
grass6: RadixColors.grassDarkP3.grass6,
grass7: RadixColors.grassDarkP3.grass7,
grass8: RadixColors.grassDarkP3.grass8,
grass9: RadixColors.grassDarkP3.grass9,
grass10: RadixColors.grassDarkP3.grass10,
grass11: RadixColors.grassDarkP3.grass11,
grass12: RadixColors.grassDarkP3.grass12,
mint1: RadixColors.mintDarkP3.mint1,
mint2: RadixColors.mintDarkP3.mint2,
mint3: RadixColors.mintDarkP3.mint3,
mint4: RadixColors.mintDarkP3.mint4,
mint5: RadixColors.mintDarkP3.mint5,
mint6: RadixColors.mintDarkP3.mint6,
mint7: RadixColors.mintDarkP3.mint7,
mint8: RadixColors.mintDarkP3.mint8,
mint9: RadixColors.mintDarkP3.mint9,
mint10: RadixColors.mintDarkP3.mint10,
mint11: RadixColors.mintDarkP3.mint11,
mint12: RadixColors.mintDarkP3.mint12,
lime1: RadixColors.limeDarkP3.lime1,
lime2: RadixColors.limeDarkP3.lime2,
lime3: RadixColors.limeDarkP3.lime3,
lime4: RadixColors.limeDarkP3.lime4,
lime5: RadixColors.limeDarkP3.lime5,
lime6: RadixColors.limeDarkP3.lime6,
lime7: RadixColors.limeDarkP3.lime7,
lime8: RadixColors.limeDarkP3.lime8,
lime9: RadixColors.limeDarkP3.lime9,
lime10: RadixColors.limeDarkP3.lime10,
lime11: RadixColors.limeDarkP3.lime11,
lime12: RadixColors.limeDarkP3.lime12,
bronze1: RadixColors.bronzeDarkP3.bronze1,
bronze2: RadixColors.bronzeDarkP3.bronze2,
bronze3: RadixColors.bronzeDarkP3.bronze3,
bronze4: RadixColors.bronzeDarkP3.bronze4,
bronze5: RadixColors.bronzeDarkP3.bronze5,
bronze6: RadixColors.bronzeDarkP3.bronze6,
bronze7: RadixColors.bronzeDarkP3.bronze7,
bronze8: RadixColors.bronzeDarkP3.bronze8,
bronze9: RadixColors.bronzeDarkP3.bronze9,
bronze10: RadixColors.bronzeDarkP3.bronze10,
bronze11: RadixColors.bronzeDarkP3.bronze11,
bronze12: RadixColors.bronzeDarkP3.bronze12,
gold1: RadixColors.goldDarkP3.gold1,
gold2: RadixColors.goldDarkP3.gold2,
gold3: RadixColors.goldDarkP3.gold3,
gold4: RadixColors.goldDarkP3.gold4,
gold5: RadixColors.goldDarkP3.gold5,
gold6: RadixColors.goldDarkP3.gold6,
gold7: RadixColors.goldDarkP3.gold7,
gold8: RadixColors.goldDarkP3.gold8,
gold9: RadixColors.goldDarkP3.gold9,
gold10: RadixColors.goldDarkP3.gold10,
gold11: RadixColors.goldDarkP3.gold11,
gold12: RadixColors.goldDarkP3.gold12,
brown1: RadixColors.brownDarkP3.brown1,
brown2: RadixColors.brownDarkP3.brown2,
brown3: RadixColors.brownDarkP3.brown3,
brown4: RadixColors.brownDarkP3.brown4,
brown5: RadixColors.brownDarkP3.brown5,
brown6: RadixColors.brownDarkP3.brown6,
brown7: RadixColors.brownDarkP3.brown7,
brown8: RadixColors.brownDarkP3.brown8,
brown9: RadixColors.brownDarkP3.brown9,
brown10: RadixColors.brownDarkP3.brown10,
brown11: RadixColors.brownDarkP3.brown11,
brown12: RadixColors.brownDarkP3.brown12,
amber1: RadixColors.amberDarkP3.amber1,
amber2: RadixColors.amberDarkP3.amber2,
amber3: RadixColors.amberDarkP3.amber3,
amber4: RadixColors.amberDarkP3.amber4,
amber5: RadixColors.amberDarkP3.amber5,
amber6: RadixColors.amberDarkP3.amber6,
amber7: RadixColors.amberDarkP3.amber7,
amber8: RadixColors.amberDarkP3.amber8,
amber9: RadixColors.amberDarkP3.amber9,
amber10: RadixColors.amberDarkP3.amber10,
amber11: RadixColors.amberDarkP3.amber11,
amber12: RadixColors.amberDarkP3.amber12,
};
@@ -0,0 +1,394 @@
import * as RadixColors from '@radix-ui/colors';
import { GRAY_SCALE_LIGHT } from './GrayScaleLight';
export const SECONDARY_COLORS_LIGHT = {
yellow1: RadixColors.yellowP3.yellow1,
yellow2: RadixColors.yellowP3.yellow2,
yellow3: RadixColors.yellowP3.yellow3,
yellow4: RadixColors.yellowP3.yellow4,
yellow5: RadixColors.yellowP3.yellow5,
yellow6: RadixColors.yellowP3.yellow6,
yellow7: RadixColors.yellowP3.yellow7,
yellow8: RadixColors.yellowP3.yellow8,
yellow9: RadixColors.yellowP3.yellow9,
yellow10: RadixColors.yellowP3.yellow10,
yellow11: RadixColors.yellowP3.yellow11,
yellow12: RadixColors.yellowP3.yellow12,
green1: RadixColors.greenP3.green1,
green2: RadixColors.greenP3.green2,
green3: RadixColors.greenP3.green3,
green4: RadixColors.greenP3.green4,
green5: RadixColors.greenP3.green5,
green6: RadixColors.greenP3.green6,
green7: RadixColors.greenP3.green7,
green8: RadixColors.greenP3.green8,
green9: RadixColors.greenP3.green9,
green10: RadixColors.greenP3.green10,
green11: RadixColors.greenP3.green11,
green12: RadixColors.greenP3.green12,
turquoise1: RadixColors.tealP3.teal1,
turquoise2: RadixColors.tealP3.teal2,
turquoise3: RadixColors.tealP3.teal3,
turquoise4: RadixColors.tealP3.teal4,
turquoise5: RadixColors.tealP3.teal5,
turquoise6: RadixColors.tealP3.teal6,
turquoise7: RadixColors.tealP3.teal7,
turquoise8: RadixColors.tealP3.teal8,
turquoise9: RadixColors.tealP3.teal9,
turquoise10: RadixColors.tealP3.teal10,
turquoise11: RadixColors.tealP3.teal11,
turquoise12: RadixColors.tealP3.teal12,
sky1: RadixColors.skyP3.sky1,
sky2: RadixColors.skyP3.sky2,
sky3: RadixColors.skyP3.sky3,
sky4: RadixColors.skyP3.sky4,
sky5: RadixColors.skyP3.sky5,
sky6: RadixColors.skyP3.sky6,
sky7: RadixColors.skyP3.sky7,
sky8: RadixColors.skyP3.sky8,
sky9: RadixColors.skyP3.sky9,
sky10: RadixColors.skyP3.sky10,
sky11: RadixColors.skyP3.sky11,
sky12: RadixColors.skyP3.sky12,
blue1: RadixColors.indigoP3.indigo1,
blue2: RadixColors.indigoP3.indigo2,
blue3: RadixColors.indigoP3.indigo3,
blue4: RadixColors.indigoP3.indigo4,
blue5: RadixColors.indigoP3.indigo5,
blue6: RadixColors.indigoP3.indigo6,
blue7: RadixColors.indigoP3.indigo7,
blue8: RadixColors.indigoP3.indigo8,
blue9: RadixColors.indigoP3.indigo9,
blue10: RadixColors.indigoP3.indigo10,
blue11: RadixColors.indigoP3.indigo11,
blue12: RadixColors.indigoP3.indigo12,
purple1: RadixColors.purpleP3.purple1,
purple2: RadixColors.purpleP3.purple2,
purple3: RadixColors.purpleP3.purple3,
purple4: RadixColors.purpleP3.purple4,
purple5: RadixColors.purpleP3.purple5,
purple6: RadixColors.purpleP3.purple6,
purple7: RadixColors.purpleP3.purple7,
purple8: RadixColors.purpleP3.purple8,
purple9: RadixColors.purpleP3.purple9,
purple10: RadixColors.purpleP3.purple10,
purple11: RadixColors.purpleP3.purple11,
purple12: RadixColors.purpleP3.purple12,
pink1: RadixColors.pinkP3.pink1,
pink2: RadixColors.pinkP3.pink2,
pink3: RadixColors.pinkP3.pink3,
pink4: RadixColors.pinkP3.pink4,
pink5: RadixColors.pinkP3.pink5,
pink6: RadixColors.pinkP3.pink6,
pink7: RadixColors.pinkP3.pink7,
pink8: RadixColors.pinkP3.pink8,
pink9: RadixColors.pinkP3.pink9,
pink10: RadixColors.pinkP3.pink10,
pink11: RadixColors.pinkP3.pink11,
pink12: RadixColors.pinkP3.pink12,
red1: RadixColors.redP3.red1,
red2: RadixColors.redP3.red2,
red3: RadixColors.redP3.red3,
red4: RadixColors.redP3.red4,
red5: RadixColors.redP3.red5,
red6: RadixColors.redP3.red6,
red7: RadixColors.redP3.red7,
red8: RadixColors.redP3.red8,
red9: RadixColors.redP3.red9,
red10: RadixColors.redP3.red10,
red11: RadixColors.redP3.red11,
red12: RadixColors.redP3.red12,
orange1: RadixColors.orangeP3.orange1,
orange2: RadixColors.orangeP3.orange2,
orange3: RadixColors.orangeP3.orange3,
orange4: RadixColors.orangeP3.orange4,
orange5: RadixColors.orangeP3.orange5,
orange6: RadixColors.orangeP3.orange6,
orange7: RadixColors.orangeP3.orange7,
orange8: RadixColors.orangeP3.orange8,
orange9: RadixColors.orangeP3.orange9,
orange10: RadixColors.orangeP3.orange10,
orange11: RadixColors.orangeP3.orange11,
orange12: RadixColors.orangeP3.orange12,
gray1: GRAY_SCALE_LIGHT.gray1,
gray2: GRAY_SCALE_LIGHT.gray2,
gray3: GRAY_SCALE_LIGHT.gray3,
gray4: GRAY_SCALE_LIGHT.gray4,
gray5: GRAY_SCALE_LIGHT.gray5,
gray6: GRAY_SCALE_LIGHT.gray6,
gray7: GRAY_SCALE_LIGHT.gray7,
gray8: GRAY_SCALE_LIGHT.gray8,
gray9: GRAY_SCALE_LIGHT.gray9,
gray10: GRAY_SCALE_LIGHT.gray10,
gray11: GRAY_SCALE_LIGHT.gray11,
gray12: GRAY_SCALE_LIGHT.gray12,
mauve1: RadixColors.mauveP3.mauve1,
mauve2: RadixColors.mauveP3.mauve2,
mauve3: RadixColors.mauveP3.mauve3,
mauve4: RadixColors.mauveP3.mauve4,
mauve5: RadixColors.mauveP3.mauve5,
mauve6: RadixColors.mauveP3.mauve6,
mauve7: RadixColors.mauveP3.mauve7,
mauve8: RadixColors.mauveP3.mauve8,
mauve9: RadixColors.mauveP3.mauve9,
mauve10: RadixColors.mauveP3.mauve10,
mauve11: RadixColors.mauveP3.mauve11,
mauve12: RadixColors.mauveP3.mauve12,
slate1: RadixColors.slateP3.slate1,
slate2: RadixColors.slateP3.slate2,
slate3: RadixColors.slateP3.slate3,
slate4: RadixColors.slateP3.slate4,
slate5: RadixColors.slateP3.slate5,
slate6: RadixColors.slateP3.slate6,
slate7: RadixColors.slateP3.slate7,
slate8: RadixColors.slateP3.slate8,
slate9: RadixColors.slateP3.slate9,
slate10: RadixColors.slateP3.slate10,
slate11: RadixColors.slateP3.slate11,
slate12: RadixColors.slateP3.slate12,
sage1: RadixColors.sageP3.sage1,
sage2: RadixColors.sageP3.sage2,
sage3: RadixColors.sageP3.sage3,
sage4: RadixColors.sageP3.sage4,
sage5: RadixColors.sageP3.sage5,
sage6: RadixColors.sageP3.sage6,
sage7: RadixColors.sageP3.sage7,
sage8: RadixColors.sageP3.sage8,
sage9: RadixColors.sageP3.sage9,
sage10: RadixColors.sageP3.sage10,
sage11: RadixColors.sageP3.sage11,
sage12: RadixColors.sageP3.sage12,
olive1: RadixColors.oliveP3.olive1,
olive2: RadixColors.oliveP3.olive2,
olive3: RadixColors.oliveP3.olive3,
olive4: RadixColors.oliveP3.olive4,
olive5: RadixColors.oliveP3.olive5,
olive6: RadixColors.oliveP3.olive6,
olive7: RadixColors.oliveP3.olive7,
olive8: RadixColors.oliveP3.olive8,
olive9: RadixColors.oliveP3.olive9,
olive10: RadixColors.oliveP3.olive10,
olive11: RadixColors.oliveP3.olive11,
olive12: RadixColors.oliveP3.olive12,
sand1: RadixColors.sandP3.sand1,
sand2: RadixColors.sandP3.sand2,
sand3: RadixColors.sandP3.sand3,
sand4: RadixColors.sandP3.sand4,
sand5: RadixColors.sandP3.sand5,
sand6: RadixColors.sandP3.sand6,
sand7: RadixColors.sandP3.sand7,
sand8: RadixColors.sandP3.sand8,
sand9: RadixColors.sandP3.sand9,
sand10: RadixColors.sandP3.sand10,
sand11: RadixColors.sandP3.sand11,
sand12: RadixColors.sandP3.sand12,
tomato1: RadixColors.tomatoP3.tomato1,
tomato2: RadixColors.tomatoP3.tomato2,
tomato3: RadixColors.tomatoP3.tomato3,
tomato4: RadixColors.tomatoP3.tomato4,
tomato5: RadixColors.tomatoP3.tomato5,
tomato6: RadixColors.tomatoP3.tomato6,
tomato7: RadixColors.tomatoP3.tomato7,
tomato8: RadixColors.tomatoP3.tomato8,
tomato9: RadixColors.tomatoP3.tomato9,
tomato10: RadixColors.tomatoP3.tomato10,
tomato11: RadixColors.tomatoP3.tomato11,
tomato12: RadixColors.tomatoP3.tomato12,
ruby1: RadixColors.rubyP3.ruby1,
ruby2: RadixColors.rubyP3.ruby2,
ruby3: RadixColors.rubyP3.ruby3,
ruby4: RadixColors.rubyP3.ruby4,
ruby5: RadixColors.rubyP3.ruby5,
ruby6: RadixColors.rubyP3.ruby6,
ruby7: RadixColors.rubyP3.ruby7,
ruby8: RadixColors.rubyP3.ruby8,
ruby9: RadixColors.rubyP3.ruby9,
ruby10: RadixColors.rubyP3.ruby10,
ruby11: RadixColors.rubyP3.ruby11,
ruby12: RadixColors.rubyP3.ruby12,
crimson1: RadixColors.crimsonP3.crimson1,
crimson2: RadixColors.crimsonP3.crimson2,
crimson3: RadixColors.crimsonP3.crimson3,
crimson4: RadixColors.crimsonP3.crimson4,
crimson5: RadixColors.crimsonP3.crimson5,
crimson6: RadixColors.crimsonP3.crimson6,
crimson7: RadixColors.crimsonP3.crimson7,
crimson8: RadixColors.crimsonP3.crimson8,
crimson9: RadixColors.crimsonP3.crimson9,
crimson10: RadixColors.crimsonP3.crimson10,
crimson11: RadixColors.crimsonP3.crimson11,
crimson12: RadixColors.crimsonP3.crimson12,
plum1: RadixColors.plumP3.plum1,
plum2: RadixColors.plumP3.plum2,
plum3: RadixColors.plumP3.plum3,
plum4: RadixColors.plumP3.plum4,
plum5: RadixColors.plumP3.plum5,
plum6: RadixColors.plumP3.plum6,
plum7: RadixColors.plumP3.plum7,
plum8: RadixColors.plumP3.plum8,
plum9: RadixColors.plumP3.plum9,
plum10: RadixColors.plumP3.plum10,
plum11: RadixColors.plumP3.plum11,
plum12: RadixColors.plumP3.plum12,
violet1: RadixColors.violetP3.violet1,
violet2: RadixColors.violetP3.violet2,
violet3: RadixColors.violetP3.violet3,
violet4: RadixColors.violetP3.violet4,
violet5: RadixColors.violetP3.violet5,
violet6: RadixColors.violetP3.violet6,
violet7: RadixColors.violetP3.violet7,
violet8: RadixColors.violetP3.violet8,
violet9: RadixColors.violetP3.violet9,
violet10: RadixColors.violetP3.violet10,
violet11: RadixColors.violetP3.violet11,
violet12: RadixColors.violetP3.violet12,
iris1: RadixColors.irisP3.iris1,
iris2: RadixColors.irisP3.iris2,
iris3: RadixColors.irisP3.iris3,
iris4: RadixColors.irisP3.iris4,
iris5: RadixColors.irisP3.iris5,
iris6: RadixColors.irisP3.iris6,
iris7: RadixColors.irisP3.iris7,
iris8: RadixColors.irisP3.iris8,
iris9: RadixColors.irisP3.iris9,
iris10: RadixColors.irisP3.iris10,
iris11: RadixColors.irisP3.iris11,
iris12: RadixColors.irisP3.iris12,
cyan1: RadixColors.cyanP3.cyan1,
cyan2: RadixColors.cyanP3.cyan2,
cyan3: RadixColors.cyanP3.cyan3,
cyan4: RadixColors.cyanP3.cyan4,
cyan5: RadixColors.cyanP3.cyan5,
cyan6: RadixColors.cyanP3.cyan6,
cyan7: RadixColors.cyanP3.cyan7,
cyan8: RadixColors.cyanP3.cyan8,
cyan9: RadixColors.cyanP3.cyan9,
cyan10: RadixColors.cyanP3.cyan10,
cyan11: RadixColors.cyanP3.cyan11,
cyan12: RadixColors.cyanP3.cyan12,
jade1: RadixColors.jadeP3.jade1,
jade2: RadixColors.jadeP3.jade2,
jade3: RadixColors.jadeP3.jade3,
jade4: RadixColors.jadeP3.jade4,
jade5: RadixColors.jadeP3.jade5,
jade6: RadixColors.jadeP3.jade6,
jade7: RadixColors.jadeP3.jade7,
jade8: RadixColors.jadeP3.jade8,
jade9: RadixColors.jadeP3.jade9,
jade10: RadixColors.jadeP3.jade10,
jade11: RadixColors.jadeP3.jade11,
jade12: RadixColors.jadeP3.jade12,
grass1: RadixColors.grassP3.grass1,
grass2: RadixColors.grassP3.grass2,
grass3: RadixColors.grassP3.grass3,
grass4: RadixColors.grassP3.grass4,
grass5: RadixColors.grassP3.grass5,
grass6: RadixColors.grassP3.grass6,
grass7: RadixColors.grassP3.grass7,
grass8: RadixColors.grassP3.grass8,
grass9: RadixColors.grassP3.grass9,
grass10: RadixColors.grassP3.grass10,
grass11: RadixColors.grassP3.grass11,
grass12: RadixColors.grassP3.grass12,
mint1: RadixColors.mintP3.mint1,
mint2: RadixColors.mintP3.mint2,
mint3: RadixColors.mintP3.mint3,
mint4: RadixColors.mintP3.mint4,
mint5: RadixColors.mintP3.mint5,
mint6: RadixColors.mintP3.mint6,
mint7: RadixColors.mintP3.mint7,
mint8: RadixColors.mintP3.mint8,
mint9: RadixColors.mintP3.mint9,
mint10: RadixColors.mintP3.mint10,
mint11: RadixColors.mintP3.mint11,
mint12: RadixColors.mintP3.mint12,
lime1: RadixColors.limeP3.lime1,
lime2: RadixColors.limeP3.lime2,
lime3: RadixColors.limeP3.lime3,
lime4: RadixColors.limeP3.lime4,
lime5: RadixColors.limeP3.lime5,
lime6: RadixColors.limeP3.lime6,
lime7: RadixColors.limeP3.lime7,
lime8: RadixColors.limeP3.lime8,
lime9: RadixColors.limeP3.lime9,
lime10: RadixColors.limeP3.lime10,
lime11: RadixColors.limeP3.lime11,
lime12: RadixColors.limeP3.lime12,
bronze1: RadixColors.bronzeP3.bronze1,
bronze2: RadixColors.bronzeP3.bronze2,
bronze3: RadixColors.bronzeP3.bronze3,
bronze4: RadixColors.bronzeP3.bronze4,
bronze5: RadixColors.bronzeP3.bronze5,
bronze6: RadixColors.bronzeP3.bronze6,
bronze7: RadixColors.bronzeP3.bronze7,
bronze8: RadixColors.bronzeP3.bronze8,
bronze9: RadixColors.bronzeP3.bronze9,
bronze10: RadixColors.bronzeP3.bronze10,
bronze11: RadixColors.bronzeP3.bronze11,
bronze12: RadixColors.bronzeP3.bronze12,
gold1: RadixColors.goldP3.gold1,
gold2: RadixColors.goldP3.gold2,
gold3: RadixColors.goldP3.gold3,
gold4: RadixColors.goldP3.gold4,
gold5: RadixColors.goldP3.gold5,
gold6: RadixColors.goldP3.gold6,
gold7: RadixColors.goldP3.gold7,
gold8: RadixColors.goldP3.gold8,
gold9: RadixColors.goldP3.gold9,
gold10: RadixColors.goldP3.gold10,
gold11: RadixColors.goldP3.gold11,
gold12: RadixColors.goldP3.gold12,
brown1: RadixColors.brownP3.brown1,
brown2: RadixColors.brownP3.brown2,
brown3: RadixColors.brownP3.brown3,
brown4: RadixColors.brownP3.brown4,
brown5: RadixColors.brownP3.brown5,
brown6: RadixColors.brownP3.brown6,
brown7: RadixColors.brownP3.brown7,
brown8: RadixColors.brownP3.brown8,
brown9: RadixColors.brownP3.brown9,
brown10: RadixColors.brownP3.brown10,
brown11: RadixColors.brownP3.brown11,
brown12: RadixColors.brownP3.brown12,
amber1: RadixColors.amberP3.amber1,
amber2: RadixColors.amberP3.amber2,
amber3: RadixColors.amberP3.amber3,
amber4: RadixColors.amberP3.amber4,
amber5: RadixColors.amberP3.amber5,
amber6: RadixColors.amberP3.amber6,
amber7: RadixColors.amberP3.amber7,
amber8: RadixColors.amberP3.amber8,
amber9: RadixColors.amberP3.amber9,
amber10: RadixColors.amberP3.amber10,
amber11: RadixColors.amberP3.amber11,
amber12: RadixColors.amberP3.amber12,
};
@@ -0,0 +1,26 @@
import { BACKGROUND_DARK } from '@new-ui/theme/constants/BackgroundDark';
import { FONT_DARK } from '@new-ui/theme/constants/FontDark';
import { MAIN_COLORS_DARK } from '@new-ui/theme/constants/MainColorsDark';
export const SNACK_BAR_DARK = {
success: {
color: MAIN_COLORS_DARK.turquoise,
backgroundColor: BACKGROUND_DARK.transparent.success,
},
error: {
color: MAIN_COLORS_DARK.red,
backgroundColor: BACKGROUND_DARK.transparent.danger,
},
warning: {
color: MAIN_COLORS_DARK.orange,
backgroundColor: BACKGROUND_DARK.transparent.orange,
},
info: {
color: MAIN_COLORS_DARK.blue,
backgroundColor: BACKGROUND_DARK.transparent.blue,
},
default: {
color: FONT_DARK.color.primary,
backgroundColor: BACKGROUND_DARK.transparent.light,
},
};
@@ -0,0 +1,26 @@
import { BACKGROUND_LIGHT } from '@new-ui/theme/constants/BackgroundLight';
import { FONT_LIGHT } from '@new-ui/theme/constants/FontLight';
import { MAIN_COLORS_LIGHT } from '@new-ui/theme/constants/MainColorsLight';
export const SNACK_BAR_LIGHT = {
success: {
color: MAIN_COLORS_LIGHT.turquoise,
backgroundColor: BACKGROUND_LIGHT.transparent.success,
},
error: {
color: MAIN_COLORS_LIGHT.red,
backgroundColor: BACKGROUND_LIGHT.transparent.danger,
},
warning: {
color: MAIN_COLORS_LIGHT.orange,
backgroundColor: BACKGROUND_LIGHT.transparent.orange,
},
info: {
color: MAIN_COLORS_LIGHT.blue,
backgroundColor: BACKGROUND_LIGHT.transparent.blue,
},
default: {
color: FONT_LIGHT.color.primary,
backgroundColor: BACKGROUND_LIGHT.transparent.light,
},
};
@@ -0,0 +1,68 @@
import { COLOR_DARK } from '@new-ui/theme/constants/ColorsDark';
export const TAG_DARK = {
text: {
gray: COLOR_DARK.gray11,
mauve: COLOR_DARK.mauve11,
slate: COLOR_DARK.slate11,
sage: COLOR_DARK.sage11,
olive: COLOR_DARK.olive11,
sand: COLOR_DARK.sand11,
tomato: COLOR_DARK.tomato11,
red: COLOR_DARK.red11,
ruby: COLOR_DARK.ruby11,
crimson: COLOR_DARK.crimson11,
pink: COLOR_DARK.pink11,
plum: COLOR_DARK.plum11,
purple: COLOR_DARK.purple11,
violet: COLOR_DARK.violet11,
iris: COLOR_DARK.iris11,
cyan: COLOR_DARK.cyan11,
turquoise: COLOR_DARK.turquoise11,
sky: COLOR_DARK.sky11,
blue: COLOR_DARK.blue11,
jade: COLOR_DARK.jade11,
green: COLOR_DARK.green11,
grass: COLOR_DARK.grass11,
mint: COLOR_DARK.mint11,
lime: COLOR_DARK.lime11,
bronze: COLOR_DARK.bronze11,
gold: COLOR_DARK.gold11,
brown: COLOR_DARK.brown11,
orange: COLOR_DARK.orange11,
amber: COLOR_DARK.amber11,
yellow: COLOR_DARK.yellow11,
},
background: {
gray: COLOR_DARK.gray3,
mauve: COLOR_DARK.mauve3,
slate: COLOR_DARK.slate3,
sage: COLOR_DARK.sage3,
olive: COLOR_DARK.olive3,
sand: COLOR_DARK.sand3,
tomato: COLOR_DARK.tomato3,
red: COLOR_DARK.red3,
ruby: COLOR_DARK.ruby3,
crimson: COLOR_DARK.crimson3,
pink: COLOR_DARK.pink3,
plum: COLOR_DARK.plum3,
purple: COLOR_DARK.purple3,
violet: COLOR_DARK.violet3,
iris: COLOR_DARK.iris3,
cyan: COLOR_DARK.cyan3,
turquoise: COLOR_DARK.turquoise3,
sky: COLOR_DARK.sky3,
blue: COLOR_DARK.blue3,
jade: COLOR_DARK.jade3,
green: COLOR_DARK.green3,
grass: COLOR_DARK.grass3,
mint: COLOR_DARK.mint3,
lime: COLOR_DARK.lime3,
bronze: COLOR_DARK.bronze3,
gold: COLOR_DARK.gold3,
brown: COLOR_DARK.brown3,
orange: COLOR_DARK.orange3,
amber: COLOR_DARK.amber3,
yellow: COLOR_DARK.yellow3,
},
};

Some files were not shown because too many files have changed in this diff Show More