diff --git a/packages/@n8n/mcp-apps/src/apps/workflow-preview/tokens.scss b/packages/@n8n/mcp-apps/src/apps/workflow-preview/tokens.scss index ed7fe24e352..bed5f36cec3 100644 --- a/packages/@n8n/mcp-apps/src/apps/workflow-preview/tokens.scss +++ b/packages/@n8n/mcp-apps/src/apps/workflow-preview/tokens.scss @@ -1,6 +1,6 @@ @use '@n8n/design-system/css/_primitives' as primitives; @use '@n8n/design-system/css/_tokens' as tokens; -@use '@n8n/design-system/css/fonts/inter'; +@use '@n8n/design-system/css/fonts/geist'; :root { color-scheme: light dark; @@ -22,13 +22,7 @@ body { margin: 0; - font-family: - InterVariable, - -apple-system, - BlinkMacSystemFont, - 'Segoe UI', - Roboto, - sans-serif; + font-family: var(--font-family); -webkit-font-smoothing: antialiased; background: transparent; color: var(--color--text--shade-1); diff --git a/packages/@n8n/mcp-apps/src/components/open-in-n8n-button.vue b/packages/@n8n/mcp-apps/src/components/open-in-n8n-button.vue index 958c4f41cf3..0d472bf7d47 100644 --- a/packages/@n8n/mcp-apps/src/components/open-in-n8n-button.vue +++ b/packages/@n8n/mcp-apps/src/components/open-in-n8n-button.vue @@ -6,11 +6,11 @@ import { useI18n } from '@mcp-apps/i18n'; withDefaults( defineProps<{ label?: string; - variant?: 'solid' | 'subtle'; + variant?: 'solid' | 'outline'; size?: 'small' | 'medium'; }>(), { - variant: 'subtle', + variant: 'outline', size: 'small', }, ); diff --git a/packages/@n8n/mcp-browser-extension/src/ui/tokens.scss b/packages/@n8n/mcp-browser-extension/src/ui/tokens.scss index 3ca56f16386..b636057896b 100644 --- a/packages/@n8n/mcp-browser-extension/src/ui/tokens.scss +++ b/packages/@n8n/mcp-browser-extension/src/ui/tokens.scss @@ -1,14 +1,12 @@ @use '@n8n/design-system/css/_primitives' as primitives; @use '@n8n/design-system/css/_tokens' as tokens; -@use '@n8n/design-system/css/fonts/inter'; +@use '@n8n/design-system/css/fonts/geist'; :root { color-scheme: light dark; @include primitives.primitives; @include tokens.theme; - - // Shared icon column, so info rows and tab rows keep one text edge and one icon centre - --row-marker-size: 40px; + --row-marker-size: var(--height--xl); } @media (prefers-color-scheme: dark) { @@ -26,25 +24,19 @@ body { margin: 0; padding: 0; - font-family: - InterVariable, - -apple-system, - BlinkMacSystemFont, - 'Segoe UI', - Roboto, - sans-serif; + font-family: var(--font-family); -webkit-font-smoothing: antialiased; background-color: var(--color--background); color: var(--color--text--shade-1); } -// Grouping box shared by the views and the allowed-instances list. +/** Keep the shared card layout for extension panels. */ .panel { display: flex; flex-direction: column; gap: var(--spacing--lg); background: var(--background--surface); - border: var(--border-width) var(--border-style) var(--color--foreground--tint-1); + border: var(--border); border-radius: var(--radius--lg); padding: var(--spacing--lg); min-height: 0; diff --git a/packages/frontend/@n8n/design-system/assets/fonts/CommitMonoVariable.woff2 b/packages/frontend/@n8n/design-system/assets/fonts/CommitMonoVariable.woff2 deleted file mode 100644 index f9ac1ee498c..00000000000 Binary files a/packages/frontend/@n8n/design-system/assets/fonts/CommitMonoVariable.woff2 and /dev/null differ diff --git a/packages/frontend/@n8n/design-system/assets/fonts/Geist-Italic-Variable.woff2 b/packages/frontend/@n8n/design-system/assets/fonts/Geist-Italic-Variable.woff2 new file mode 100644 index 00000000000..58b157a8a6c Binary files /dev/null and b/packages/frontend/@n8n/design-system/assets/fonts/Geist-Italic-Variable.woff2 differ diff --git a/packages/frontend/@n8n/design-system/assets/fonts/Geist-OFL.txt b/packages/frontend/@n8n/design-system/assets/fonts/Geist-OFL.txt new file mode 100644 index 00000000000..04e95fc5508 --- /dev/null +++ b/packages/frontend/@n8n/design-system/assets/fonts/Geist-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/packages/frontend/@n8n/design-system/assets/fonts/Geist-Variable.woff2 b/packages/frontend/@n8n/design-system/assets/fonts/Geist-Variable.woff2 new file mode 100644 index 00000000000..5c5999d617e Binary files /dev/null and b/packages/frontend/@n8n/design-system/assets/fonts/Geist-Variable.woff2 differ diff --git a/packages/frontend/@n8n/design-system/assets/fonts/GeistMono-Italic-Variable.woff2 b/packages/frontend/@n8n/design-system/assets/fonts/GeistMono-Italic-Variable.woff2 new file mode 100644 index 00000000000..c663ca57222 Binary files /dev/null and b/packages/frontend/@n8n/design-system/assets/fonts/GeistMono-Italic-Variable.woff2 differ diff --git a/packages/frontend/@n8n/design-system/assets/fonts/GeistMono-Variable.woff2 b/packages/frontend/@n8n/design-system/assets/fonts/GeistMono-Variable.woff2 new file mode 100644 index 00000000000..efaf95dba19 Binary files /dev/null and b/packages/frontend/@n8n/design-system/assets/fonts/GeistMono-Variable.woff2 differ diff --git a/packages/frontend/@n8n/design-system/assets/fonts/InterVariable-Italic.woff2 b/packages/frontend/@n8n/design-system/assets/fonts/InterVariable-Italic.woff2 deleted file mode 100644 index b3530f3f526..00000000000 Binary files a/packages/frontend/@n8n/design-system/assets/fonts/InterVariable-Italic.woff2 and /dev/null differ diff --git a/packages/frontend/@n8n/design-system/assets/fonts/InterVariable.woff2 b/packages/frontend/@n8n/design-system/assets/fonts/InterVariable.woff2 deleted file mode 100644 index 5a8d3e72ad7..00000000000 Binary files a/packages/frontend/@n8n/design-system/assets/fonts/InterVariable.woff2 and /dev/null differ diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue index 0841f784cf8..9f885681bff 100644 --- a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue +++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/AskAssistantChat.vue @@ -557,7 +557,7 @@ defineExpose({ > diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/__snapshots__/AskAssistantChat.test.ts.snap b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/__snapshots__/AskAssistantChat.test.ts.snap index 61f9d4d39af..d5b9c8b056e 100644 --- a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/__snapshots__/AskAssistantChat.test.ts.snap +++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/__snapshots__/AskAssistantChat.test.ts.snap @@ -289,7 +289,7 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = ` icononly="false" loading="false" size="xsmall" - variant="subtle" + variant="outline" />
renders chat with messages correctly 1`] = ` icononly="false" loading="false" size="xsmall" - variant="subtle" + variant="outline" />
diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BlockMessage.stories.ts b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BlockMessage.stories.ts new file mode 100644 index 00000000000..7e6f234caf9 --- /dev/null +++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BlockMessage.stories.ts @@ -0,0 +1,40 @@ +import type { StoryFn } from '@storybook/vue3-vite'; + +import BlockMessage from './BlockMessage.vue'; + +export default { + title: 'Areas/Assistant/BlockMessage', + component: BlockMessage, + parameters: { + docs: { + description: { + component: 'A titled summary block in Assistant chat, with markdown body content.', + }, + }, + }, +}; + +const Template: StoryFn = (args) => ({ + setup: () => ({ args }), + components: { BlockMessage }, + template: ` +
+ +
+ `, +}); + +export const Default = Template.bind({}); +Default.args = { + isFirstOfRole: true, + user: { firstName: 'Max', lastName: 'Test' }, + message: { + id: 'block-1', + role: 'assistant', + type: 'block', + title: "Credential doesn't have correct permissions to send a message", + content: + '### Solution steps\n1. Check the credential **scopes**\n2. Reconnect the account\n3. Retry the node\n\n- Unordered item 1\n- Unordered item 2', + read: false, + }, +}; diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BlockMessage.vue b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BlockMessage.vue index 535d7b2c07c..91fdc826083 100644 --- a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BlockMessage.vue +++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/BlockMessage.vue @@ -61,7 +61,7 @@ const { renderMarkdown } = useMarkdown(); .blockTitle { border-bottom: var(--border); padding: var(--spacing--2xs); - font-weight: var(--font-weight--bold); + font-weight: var(--font-weight--medium); } .blockBody { @@ -77,7 +77,7 @@ const { renderMarkdown } = useMarkdown(); h1, h2, h3 { - font-weight: var(--font-weight--bold); + font-weight: var(--font-weight--medium); font-size: var(--font-size--xs); margin: var(--spacing--xs) 0 var(--spacing--4xs); } @@ -85,7 +85,7 @@ const { renderMarkdown } = useMarkdown(); h4, h5, h6 { - font-weight: var(--font-weight--bold); + font-weight: var(--font-weight--medium); font-size: var(--font-size--2xs); } diff --git a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/ErrorMessage.vue b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/ErrorMessage.vue index 15014646a6f..75e1d8d49b9 100644 --- a/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/ErrorMessage.vue +++ b/packages/frontend/@n8n/design-system/src/components/AskAssistantChat/messages/ErrorMessage.vue @@ -27,7 +27,7 @@ const { t } = useI18n();

{{ message }} - + {{ t('collaboration.canvas.acquireEditing') }} diff --git a/packages/frontend/@n8n/design-system/src/components/CodeDiff/CodeDiff.vue b/packages/frontend/@n8n/design-system/src/components/CodeDiff/CodeDiff.vue index 26fe46c11b2..5322df45e82 100644 --- a/packages/frontend/@n8n/design-system/src/components/CodeDiff/CodeDiff.vue +++ b/packages/frontend/@n8n/design-system/src/components/CodeDiff/CodeDiff.vue @@ -113,7 +113,7 @@ const diffs = computed(() => {
{
{ .title { padding: var(--spacing--2xs); - font-weight: var(--font-weight--bold); + font-weight: var(--font-weight--medium); font-size: var(--font-size--2xs); // ensure consistent spacing even if title is empty min-height: 32.5px; diff --git a/packages/frontend/@n8n/design-system/src/components/CodeDiff/__snapshots__/CodeDiff.test.ts.snap b/packages/frontend/@n8n/design-system/src/components/CodeDiff/__snapshots__/CodeDiff.test.ts.snap index 478dc404046..87a1ea663a7 100644 --- a/packages/frontend/@n8n/design-system/src/components/CodeDiff/__snapshots__/CodeDiff.test.ts.snap +++ b/packages/frontend/@n8n/design-system/src/components/CodeDiff/__snapshots__/CodeDiff.test.ts.snap @@ -854,7 +854,7 @@ exports[`CodeDiff > renders replaced code diff correctly 1`] = ` icononly="false" loading="false" size="xsmall" - variant="subtle" + variant="outline" /> renders replacing code diff correctly 1`] = ` icononly="false" loading="true" size="xsmall" - variant="subtle" + variant="outline" /> diff --git a/packages/frontend/@n8n/design-system/src/components/DateRangePicker/DateRangePicker.vue b/packages/frontend/@n8n/design-system/src/components/DateRangePicker/DateRangePicker.vue index 3f833e84228..2996b371948 100644 --- a/packages/frontend/@n8n/design-system/src/components/DateRangePicker/DateRangePicker.vue +++ b/packages/frontend/@n8n/design-system/src/components/DateRangePicker/DateRangePicker.vue @@ -48,7 +48,7 @@ const forwarded = useForwardPropsEmits(props, emit); - + @@ -63,11 +63,11 @@ const forwarded = useForwardPropsEmits(props, emit);
- + - + @@ -116,7 +116,7 @@ const forwarded = useForwardPropsEmits(props, emit); { }, }); - expect(getByRole('button').querySelector('svg')).toHaveAttribute('data-icon', 'ellipsis'); + expect(getByRole('button').querySelector('svg')).toHaveAttribute('data-icon', 'ellipsis-12'); }); it('should have bottom-end as default placement', async function () { const { container, getByRole } = render(ActionToggle, { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nActionToggle/ActionToggle.vue b/packages/frontend/@n8n/design-system/src/components/N8nActionToggle/ActionToggle.vue index 5bf78d416cd..db90158a81a 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nActionToggle/ActionToggle.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nActionToggle/ActionToggle.vue @@ -101,7 +101,7 @@ defineExpose({ { .title { font-size: var.$alert-title-font-size; line-height: 18px; - font-weight: var(--font-weight--bold); + font-weight: var(--font-weight--medium); } .description { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nAlertDialog/AlertDialog.vue b/packages/frontend/@n8n/design-system/src/components/N8nAlertDialog/AlertDialog.vue index f3747dd9d5a..9dfcc6d1daf 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nAlertDialog/AlertDialog.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nAlertDialog/AlertDialog.vue @@ -109,7 +109,7 @@ const handleCancel = () => { - + AVATAR_SIZES[size]; .initials { position: absolute; font-size: var(--font-size--2xs); - font-weight: var(--font-weight--bold); + font-weight: var(--font-weight--medium); color: var(--avatar--color--text); text-shadow: 0 1px 6px rgba(25, 11, 9, 0.3); text-transform: uppercase; diff --git a/packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.stories.ts b/packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.stories.ts index 3079582c137..1dbb2d7417e 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.stories.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.stories.ts @@ -99,16 +99,28 @@ export const Sizes: Story = { }, components: { N8nBadge }, template: ` -
- - Active - +
+
+ + Active + +
+
+ + Active + +
`, }; diff --git a/packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.vue b/packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.vue index bcb5d67f0a3..7136749f1e5 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nBadge/Badge.vue @@ -79,7 +79,6 @@ const effectiveTextSize = computed(function getEffectiveTextSize() { display: inline-flex; align-items: center; white-space: nowrap; - border-radius: var(--radius--full); user-select: none; appearance: none; width: fit-content; @@ -91,7 +90,10 @@ const effectiveTextSize = computed(function getEffectiveTextSize() { --n8n-badge--padding-inline-start: var(--spacing--2xs); --n8n-badge--padding-inline-end: var(--spacing--2xs); --n8n-badge--gap: var(--spacing--4xs); + --n8n-badge--radius: var(--radius--xs); + --n8n-badge--label-shift: 0; + border-radius: var(--n8n-badge--radius); gap: var(--n8n-badge--gap); background-color: var(--n8n-badge--background); border: 1px solid var(--n8n-badge--border-color); @@ -102,6 +104,7 @@ const effectiveTextSize = computed(function getEffectiveTextSize() { .label { @include utils.utils-ellipsis; + transform: translateY(var(--n8n-badge--label-shift)); } .clickable { @@ -130,36 +133,45 @@ const effectiveTextSize = computed(function getEffectiveTextSize() { --n8n-badge--height: var(--height--2xs); --n8n-badge--padding-inline-start: var(--spacing--2xs); --n8n-badge--padding-inline-end: var(--spacing--2xs); + --n8n-badge--radius: var(--radius--2xs); + --n8n-badge--label-shift: 1px; } .xsmall { --n8n-badge--height: var(--height--xs); --n8n-badge--padding-inline-start: var(--spacing--2xs); --n8n-badge--padding-inline-end: var(--spacing--2xs); + --n8n-badge--radius: var(--radius--2xs); + --n8n-badge--label-shift: 1px; } .small { --n8n-badge--height: var(--height--sm); --n8n-badge--padding-inline-start: var(--spacing--2xs); --n8n-badge--padding-inline-end: var(--spacing--2xs); + --n8n-badge--label-shift: 0; } .medium { --n8n-badge--height: var(--height--md); --n8n-badge--padding-inline-start: var(--spacing--xs); --n8n-badge--padding-inline-end: var(--spacing--xs); + --n8n-badge--label-shift: 0; } .large { --n8n-badge--height: var(--height--lg); --n8n-badge--padding-inline-start: var(--spacing--sm); --n8n-badge--padding-inline-end: var(--spacing--sm); + --n8n-badge--label-shift: 0; } .xlarge { --n8n-badge--height: var(--height--xl); --n8n-badge--padding-inline-start: var(--spacing--sm); --n8n-badge--padding-inline-end: var(--spacing--sm); + --n8n-badge--radius: var(--radius--sm); + --n8n-badge--label-shift: 0; } .filled { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nBreadcrumbs/__snapshots__/BreadCrumbs.test.ts.snap b/packages/frontend/@n8n/design-system/src/components/N8nBreadcrumbs/__snapshots__/BreadCrumbs.test.ts.snap index da857b6fdae..8e3584bca1e 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nBreadcrumbs/__snapshots__/BreadCrumbs.test.ts.snap +++ b/packages/frontend/@n8n/design-system/src/components/N8nBreadcrumbs/__snapshots__/BreadCrumbs.test.ts.snap @@ -6,11 +6,11 @@ exports[`Breadcrumbs > does not highlight last item for "highlightLastItem = fal -
  • Folder 1
  • +
  • Folder 1
  • /
  • -
  • Folder 2
  • +
  • Folder 2
  • /
  • -
  • Folder 3
  • +
  • Folder 3
  • /
  • Current
  • @@ -24,11 +24,11 @@ exports[`Breadcrumbs > renders custom separator correctly 1`] = ` -
  • Folder 1
  • +
  • Folder 1
  • ➮
  • -
  • Folder 2
  • +
  • Folder 2
  • ➮
  • -
  • Folder 3
  • +
  • Folder 3
  • ➮
  • Current
  • @@ -42,11 +42,11 @@ exports[`Breadcrumbs > renders default version correctly 1`] = ` -
  • Folder 1
  • +
  • Folder 1
  • /
  • -
  • Folder 2
  • +
  • Folder 2
  • /
  • -
  • Folder 3
  • +
  • Folder 3
  • /
  • Current
  • @@ -61,11 +61,11 @@ exports[`Breadcrumbs > renders slots correctly 1`] = `
  • /
  • -
  • Folder 1
  • +
  • Folder 1
  • /
  • -
  • Folder 2
  • +
  • Folder 2
  • /
  • -
  • Folder 3
  • +
  • Folder 3
  • /
  • Current
  • @@ -80,11 +80,11 @@ exports[`Breadcrumbs > renders small version correctly 1`] = ` -
  • Folder 1
  • +
  • Folder 1
  • /
  • -
  • Folder 2
  • +
  • Folder 2
  • /
  • -
  • Folder 3
  • +
  • Folder 3
  • /
  • Current
  • diff --git a/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.stories.ts b/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.stories.ts index 035bbfd3d3e..7000f99853e 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.stories.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.stories.ts @@ -11,7 +11,7 @@ const meta = { argTypes: { variant: { control: 'select', - options: ['solid', 'subtle', 'ghost', 'outline', 'destructive', 'success'], + options: ['solid', 'brand', 'ghost', 'outline', 'destructive', 'success'], }, size: { control: 'select', @@ -73,7 +73,7 @@ export const Variants: Story = { template: `
    Solid - Subtle + Brand Outline Ghost Destructive @@ -130,7 +130,7 @@ export const Loading: Story = { template: `
    Solid - Subtle + Brand Outline Ghost Destructive @@ -147,7 +147,7 @@ export const Link: Story = { template: `
    Link - Link + Link Link Link Link @@ -181,7 +181,7 @@ export const Disabled: Story = { template: `
    Solid - Subtle + Brand Outline Ghost Destructive @@ -200,7 +200,7 @@ export const Disabled: Story = { * | Legacy `type` | Current `variant` | * |---------------|-------------------| * | `primary` | `solid` | - * | `secondary` | `subtle` | + * | `secondary` | `outline` | * | `tertiary` | `ghost` | * | `danger` | `destructive` | * | `success` | `success` | @@ -224,8 +224,8 @@ export const TypeToVariantMapping: Story = { Solid type="secondary" - variant="subtle" - Subtle + variant="outline" + Outline type="tertiary" variant="ghost" @@ -261,7 +261,6 @@ export const IconButtonPatterns: Story = { - diff --git a/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.test.ts index a8a32155e65..153f534e1ea 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.test.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.test.ts @@ -7,6 +7,11 @@ const stubs = ['N8nSpinner', 'N8nIcon']; describe('components', () => { describe('N8nButton', () => { + it('maps the legacy subtle variant to brand', () => { + const wrapper = render(N8nButton, { props: { variant: 'subtle' }, global: { stubs } }); + expect(wrapper.getByRole('button').className).toContain('brand'); + }); + describe('rendering', () => { it('should render correctly with default props', () => { const wrapper = render(N8nButton, { @@ -82,7 +87,7 @@ describe('components', () => { describe('props', () => { describe('variant', () => { - it.each(['solid', 'subtle', 'ghost', 'outline', 'destructive', 'success'] as const)( + it.each(['solid', 'brand', 'ghost', 'outline', 'destructive', 'success'] as const)( 'should render %s variant', (variant) => { const wrapper = render(N8nButton, { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.vue b/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.vue index dab2538be5a..2d2549b6b45 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nButton/Button.vue @@ -29,6 +29,7 @@ const effectiveSize = computed(() => { // Map legacy variant values to current ones const effectiveVariant = computed(() => { + if (props.variant === 'subtle') return 'brand'; if (props.variant === 'highlight') return 'ghost'; if (props.variant === 'highlight-fill') return 'ghost'; return props.variant; @@ -145,7 +146,7 @@ const handleClick = (event: MouseEvent) => { height: var(--button--height); padding: var(--button--padding); - border-radius: var(--button--radius); + border-radius: var(--radius--full); font-size: var(--button--font-size); --button--color--background: transparent; @@ -201,43 +202,72 @@ const handleClick = (event: MouseEvent) => { &.xsmall { --button--height: var(--height--xs); --button--padding: 0 var(--spacing--2xs); - --button--radius: var(--radius--3xs); + --button--radius: var(--radius--xs); --button--font-size: var(--font-size--2xs); } &.small { --button--height: var(--height--sm); --button--padding: 0 var(--spacing--xs); - --button--radius: var(--radius--3xs); + --button--radius: var(--radius--xs); --button--font-size: var(--font-size--xs); } &.medium { --button--height: var(--height--md); --button--padding: 0 var(--spacing--xs); - --button--radius: var(--radius--3xs); + --button--radius: var(--radius--xs); --button--font-size: var(--font-size--sm); } &.large { --button--height: var(--height--lg); --button--padding: 0 var(--spacing--sm); - --button--radius: var(--radius--2xs); + --button--radius: var(--radius--sm); --button--font-size: var(--font-size--sm); } &.xlarge { --button--height: var(--height--xl); --button--padding: 0 var(--spacing--sm); - --button--radius: var(--radius--xs); + --button--radius: var(--radius--sm); --button--font-size: var(--font-size--md); } &.solid { + --button--color--background: var(--color--primary); + --button--color--background-hover: color-mix( + in srgb, + var(--color--primary), + light-dark(var(--color--neutral-white), var(--color--neutral-black)) 10% + ); + --button--color--background-active: color-mix( + in srgb, + var(--color--primary), + light-dark(var(--color--neutral-white), var(--color--neutral-black)) 12% + ); + --button--color: var(--text-color--inverse); + --button--border-color: var(--color--primary); + --button--border-color--hover: color-mix( + in srgb, + var(--button--border-color), + light-dark(var(--color--neutral-white), var(--color--neutral-black)) 10% + ); + --button--border-color--active: color-mix( + in srgb, + var(--button--border-color), + light-dark(var(--color--neutral-white), var(--color--neutral-black)) 12% + ); + --button--border--shadow: 0 0 0 1px var(--button--border-color); + --button--border--shadow--hover: 0 0 0 1px var(--button--border-color--hover); + --button--border--shadow--active: 0 0 0 1px var(--button--border-color--active); + } + + &.brand { --button--color--background: var(--background--brand); --button--color--background-hover: var(--background--brand--hover); --button--color--background-active: var(--background--brand--active); - --button--color: var(--color--neutral-white); + --button--color: var(--color--neutral-black); --button--shadow: var(--shadow--xs); --button--shadow--hover: var(--shadow--xs); --button--shadow--active: var(--shadow--xs); @@ -249,7 +279,7 @@ const handleClick = (event: MouseEvent) => { --button--border--shadow--active: 0 0 0 1px var(--button--border-color--active); } - &.subtle { + &.outline { --button--color--background: var(--background--surface); --button--color--background-hover: color-mix( in srgb, @@ -261,34 +291,20 @@ const handleClick = (event: MouseEvent) => { var(--button--color--background), light-dark(var(--color--neutral-black), var(--color--neutral-white)) 10% ); + --button--border-color: var(--border-color); + --button--border-color--hover: color-mix( + in srgb, + var(--button--border-color), + light-dark(var(--color--neutral-black), var(--color--neutral-white)) 5% + ); + --button--border-color--active: color-mix( + in srgb, + var(--button--border-color), + light-dark(var(--color--neutral-black), var(--color--neutral-white)) 10% + ); --button--shadow: var(--shadow--xs); --button--shadow--hover: var(--shadow--xs); --button--shadow--active: var(--shadow--xs); - --button--border-color: var(--border-color); - --button--border-color--hover: light-dark( - var(--color--black-alpha-200), - var(--color--white-alpha-200) - ); - --button--border-color--active: light-dark( - var(--color--black-alpha-300), - var(--color--white-alpha-300) - ); - --button--border--shadow: 0 0 0 1px var(--button--border-color); - --button--border--shadow--hover: 0 0 0 1px var(--button--border-color--hover); - --button--border--shadow--active: 0 0 0 1px var(--button--border-color--active); - } - - &.outline { - --button--color--background: transparent; - --button--border-color: var(--border-color); - --button--border-color--hover: light-dark( - var(--color--black-alpha-200), - var(--color--white-alpha-200) - ); - --button--border-color--active: light-dark( - var(--color--black-alpha-300), - var(--color--white-alpha-300) - ); --button--border--shadow: 0 0 0 1px var(--button--border-color); --button--border--shadow--hover: 0 0 0 1px var(--button--border-color--hover); --button--border--shadow--active: 0 0 0 1px var(--button--border-color--active); @@ -416,28 +432,11 @@ const handleClick = (event: MouseEvent) => { @include motion.spin; } -/* TODO: Move to global animations css library */ -:global(.n8n-button-fade-enter-active), -:global(.n8n-button-fade-leave-active) { - --easing--ease-out: cubic-bezier(0.215, 0.61, 0.355, 1); - transition: - opacity 0.2s var(--easing--ease-out), - transform 0.2s var(--easing--ease-out); - - @media (prefers-reduced-motion: reduce) { - transition: opacity 0.1s; - } +:global(.n8n-button-fade-enter-active) { + @include motion.fade-in-up; } -:global(.n8n-button-fade-enter-from), -:global(.n8n-button-fade-leave-to) { - opacity: 0; - transform: translateY(4px); - filter: blur(2px); - - @media (prefers-reduced-motion: reduce) { - transform: none; - filter: none; - } +:global(.n8n-button-fade-leave-active) { + @include motion.fade-out-down; } diff --git a/packages/frontend/@n8n/design-system/src/components/N8nButtonList/ButtonList.stories.ts b/packages/frontend/@n8n/design-system/src/components/N8nButtonList/ButtonList.stories.ts index 77745e2c6e0..cfb0d7cada5 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nButtonList/ButtonList.stories.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nButtonList/ButtonList.stories.ts @@ -42,7 +42,7 @@ export const Default: Story = { }, template: ` - Save + Save Publish More @@ -61,9 +61,9 @@ export const IconButtons: Story = { }, template: ` - - - + + + `, }), @@ -80,9 +80,9 @@ export const Vertical: Story = { }, template: ` - - - + + + `, }), diff --git a/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue b/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue index 77a0e328d1b..1adad0d2171 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nCallout/Callout.vue @@ -170,11 +170,15 @@ const getIconSize = computed(() => { .secondary { font-size: var(--font-size--2xs); - font-weight: var(--font-weight--bold); + font-weight: var(--font-weight--medium); border-color: var(--callout--border-color--secondary); background-color: var(--callout--color--background--secondary); color: var(--callout--color--text--secondary); + .messageSection > :global(.n8n-text) { + font-weight: inherit; + } + .icon { color: var(--callout--icon-color--secondary); } diff --git a/packages/frontend/@n8n/design-system/src/components/N8nCard/Card.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nCard/Card.test.ts index 544ce61c237..10a00bbfb69 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nCard/Card.test.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nCard/Card.test.ts @@ -1,9 +1,37 @@ import { render } from '@testing-library/vue'; +import { mount } from '@vue/test-utils'; +import { defineComponent, h, nextTick, ref } from 'vue'; import N8nCard from './Card.vue'; describe('components', () => { describe('N8nCard', () => { + it('updates hover classes when the click listener changes', async () => { + const onClick = vi.fn(); + const clickHandler = ref(); + const hoverable = ref(false); + const wrapper = mount( + defineComponent({ + setup: () => () => + h(N8nCard, { onClick: clickHandler.value, hoverable: hoverable.value }), + }), + ); + const card = wrapper.getComponent(N8nCard); + expect(card.classes()).not.toContain('hoverable'); + clickHandler.value = onClick; + await nextTick(); + expect(card.classes()).toContain('hoverable'); + await card.trigger('click'); + expect(onClick).toHaveBeenCalledOnce(); + clickHandler.value = undefined; + await nextTick(); + expect(card.classes()).not.toContain('hoverable'); + hoverable.value = true; + await nextTick(); + expect(card.classes()).toContain('hoverable'); + wrapper.unmount(); + }); + it('should render correctly', () => { const wrapper = render(N8nCard, { slots: { diff --git a/packages/frontend/@n8n/design-system/src/components/N8nCard/Card.vue b/packages/frontend/@n8n/design-system/src/components/N8nCard/Card.vue index d5e8fee1b71..a2216d319a4 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nCard/Card.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nCard/Card.vue @@ -1,5 +1,5 @@