🎉 Add detach to typography and shadow tokens (#11969)

* 🎉 Add detach to typography tokens

* 🎉 Add detach to shadow tokens

* 🎉 Add tests
This commit is contained in:
Eva Marco 2026-10-05 11:32:29 +02:00 committed by GitHub
parent 7c039231f7
commit b06a43404b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 500 additions and 85 deletions

View File

@ -0,0 +1,194 @@
import { test, expect } from "@playwright/test";
import { WasmWorkspacePage } from "../../pages/WasmWorkspacePage";
import { BaseWebSocketPage } from "../../pages/BaseWebSocketPage";
import {
setupEmptyTokensFileRender,
setupTypographyTokensFileRender,
unfoldTokenType,
} from "./helpers";
test.beforeEach(async ({ page }) => {
await WasmWorkspacePage.init(page);
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-tokens.json");
});
const openEditModal = async (page, tokensTabPanel, name) => {
await tokensTabPanel
.getByRole("button", { name, exact: true })
.click({ button: "right" });
await page.getByText("Edit token").click();
};
// Hovers the reference input and clicks the detach button, which is only
// visible while the reference row is hovered or focused.
const detachReference = async (modal) => {
const referenceField = modal.getByRole("textbox", { name: "Reference" });
const detachButton = modal.getByRole("button", { name: "Detach reference" });
await expect(referenceField).toBeVisible();
await expect(detachButton).toBeHidden();
await referenceField.hover();
await expect(detachButton).toBeVisible();
await detachButton.click();
};
test.describe("Tokens - detach reference", () => {
test("User detaches a typography reference and edits the copied values", async ({
page,
}) => {
const { tokensUpdateCreateModal, tokensSidebar } =
await setupTypographyTokensFileRender(page);
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
const saveButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
// Create a typography token that references "Full" from the fixture.
await tokensTabPanel
.getByRole("button", { name: "Add Token: Typography" })
.click();
await expect(tokensUpdateCreateModal).toBeVisible();
await tokensUpdateCreateModal.getByLabel("Name").fill("detached");
await tokensUpdateCreateModal.getByTestId("reference-opt").click();
await tokensUpdateCreateModal
.getByRole("textbox", { name: "Reference" })
.fill("{Full}");
await expect(saveButton).toBeEnabled();
await saveButton.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
await unfoldTokenType(tokensSidebar, "typography");
await openEditModal(page, tokensTabPanel, "detached");
await expect(tokensUpdateCreateModal).toBeVisible();
await detachReference(tokensUpdateCreateModal);
// The values of "Full" are now in the composite tab.
const fontFamilyField = tokensUpdateCreateModal
.getByRole("textbox", { name: "Font family" })
.first();
const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Font Size",
});
await expect(fontFamilyField).toHaveValue("42dot Sans");
await expect(fontSizeField).toHaveValue("100");
await expect(
tokensUpdateCreateModal.getByRole("textbox", { name: "Font Weight" }),
).toHaveValue("300");
await expect(
tokensUpdateCreateModal.getByRole("textbox", { name: "Line Height" }),
).toHaveValue("2");
await expect(
tokensUpdateCreateModal.getByRole("textbox", { name: "Letter Spacing" }),
).toHaveValue("2");
await expect(
tokensUpdateCreateModal.getByRole("textbox", { name: "Text Case" }),
).toHaveValue("uppercase");
await expect(
tokensUpdateCreateModal.getByRole("textbox", { name: "Text Decoration" }),
).toHaveValue("underline");
await fontSizeField.fill("24");
await expect(saveButton).toBeEnabled();
await saveButton.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
// The detached token keeps its own values.
await openEditModal(page, tokensTabPanel, "detached");
await expect(fontSizeField).toHaveValue("24");
await tokensUpdateCreateModal
.getByRole("button", { name: "Cancel" })
.click();
// The referenced token is not modified.
await openEditModal(page, tokensTabPanel, "Full");
await expect(fontSizeField).toHaveValue("100");
});
test("User detaches a shadow reference and edits the copied values", async ({
page,
}) => {
const { tokensUpdateCreateModal } = await setupEmptyTokensFileRender(page, {
flags: ["enable-token-shadow"],
});
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
const addShadowButton = tokensTabPanel.getByRole("button", {
name: "Add Token: Shadow",
});
const nameField = tokensUpdateCreateModal.getByLabel("Name");
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Color",
});
const offsetXField = tokensUpdateCreateModal.getByRole("textbox", {
name: "X",
});
const offsetYField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Y",
});
const blurField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Blur",
});
const spreadField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Spread",
});
const saveButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
// Base shadow token.
await addShadowButton.click();
await expect(tokensUpdateCreateModal).toBeVisible();
await nameField.fill("base-shadow");
await colorField.fill("red");
await offsetXField.fill("2");
await offsetYField.fill("3");
await blurField.fill("5");
await spreadField.fill("1");
await expect(saveButton).toBeEnabled();
await saveButton.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
// Shadow token that references the base one.
await addShadowButton.click();
await expect(tokensUpdateCreateModal).toBeVisible();
await nameField.fill("alias-shadow");
await tokensUpdateCreateModal.getByTestId("reference-opt").click();
await tokensUpdateCreateModal
.getByRole("textbox", { name: "Reference" })
.fill("{base-shadow}");
await expect(saveButton).toBeEnabled();
await saveButton.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
await unfoldTokenType(tokensTabPanel, "shadow");
await openEditModal(page, tokensTabPanel, "alias-shadow");
await expect(tokensUpdateCreateModal).toBeVisible();
await detachReference(tokensUpdateCreateModal);
// The values of "base-shadow" are now in the shadow tab.
await expect(colorField).toHaveValue("red");
await expect(offsetXField).toHaveValue("2");
await expect(offsetYField).toHaveValue("3");
await expect(blurField).toHaveValue("5");
await expect(spreadField).toHaveValue("1");
await offsetXField.fill("10");
await expect(saveButton).toBeEnabled();
await saveButton.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
// The detached token keeps its own values.
await openEditModal(page, tokensTabPanel, "alias-shadow");
await expect(offsetXField).toHaveValue("10");
await tokensUpdateCreateModal
.getByRole("button", { name: "Cancel" })
.click();
// The referenced token is not modified.
await openEditModal(page, tokensTabPanel, "base-shadow");
await expect(offsetXField).toHaveValue("2");
});
});

View File

@ -125,3 +125,30 @@
[error token-name]
(let [translated-string (tr "errors.tokens.name-collision" token-name)]
(= error translated-string)))
(defn- referenced-token-name
"Name of the token `reference` points to, when `reference` is exactly
one `{name}` reference."
[reference]
(when (string? reference)
(let [reference (str/trim reference)
names (cto/find-token-value-references reference)
name (first names)]
(when (and (= 1 (count names))
(= reference (str "{" name "}")))
name))))
(defn referenced-token-value
"Composite value of the `token-type` token that `reference` points to,
following chained references. Nil when the chain does not end in a
`token-type` token with a composite value."
[tokens reference token-type]
(loop [reference reference
visited #{}]
(when-let [name (referenced-token-name reference)]
(let [{:keys [type value]} (get tokens name)]
(when (and (= token-type type)
(not (contains? visited name)))
(if (string? value)
(recur value (conj visited name))
value))))))

View File

@ -18,6 +18,7 @@
[app.main.ui.forms :as forms]
[app.main.ui.hooks :as hooks]
[app.main.ui.workspace.tokens.management.forms.controls :as token.controls]
[app.main.ui.workspace.tokens.management.forms.controls.utils :as csu]
[app.main.ui.workspace.tokens.management.forms.generic-form :as generic]
[app.main.ui.workspace.tokens.management.forms.validators :refer [check-self-reference default-validate-token]]
[app.util.dom :as dom]
@ -75,6 +76,25 @@
:blur "4"
:spread "0"})
(defn- make-default-value
[value]
(cond
(string? value)
{:reference value
:shadow []}
(vector? value)
{:reference nil
:shadow (mapv (fn [shadow]
(-> shadow
(update :blur #(if (str/blank? %) "0" %))
(update :spread #(if (str/blank? %) "0" %))))
value)}
:else
{:reference nil
:shadow [default-token-shadow]}))
(defn get-subtoken
[token index prop value-subfield]
(let [value (get-in token [:value value-subfield index prop])]
@ -202,19 +222,66 @@
:show-button (> length 1)}])))
(mf/defc reference-form*
[{:keys [token tokens] :as props}]
[:div {:class (stl/css :input-row-reference)}
[:> token.controls/input-composite*
{:placeholder (tr "workspace.tokens.reference-composite-shadow")
:aria-label (tr "labels.reference")
:icon i/drop-shadow
:name :reference
:token token
:tokens tokens}]])
[{:keys [token tokens on-detach] :as props}]
(let [form (mf/use-ctx forms/context)
reference (get-in @form [:data :value :reference])
detached-value
(mf/with-memo [tokens reference]
(csu/referenced-token-value tokens reference :shadow))
on-detach-click
(mf/use-fn
(mf/deps detached-value on-detach)
(fn [event]
(dom/prevent-default event)
(on-detach detached-value)))]
[:div {:class (stl/css :input-row-reference :reference-row)}
[:> token.controls/input-composite*
{:placeholder (tr "workspace.tokens.reference-composite-shadow")
:aria-label (tr "labels.reference")
:icon i/drop-shadow
:name :reference
:token token
:tokens tokens
:slot-end (when (some? detached-value)
(mf/html
[:> icon-button* {:variant "action"
:type "button"
:class (stl/css :detach-button)
:aria-label (tr "workspace.tokens.detach-reference")
:on-click on-detach-click
:icon i/detach}]))}]]))
(mf/defc tabs-wrapper*
[{:keys [token tokens tab handle-toggle value-subfield] :rest props}]
(let [form (mf/use-ctx forms/context)
;; Form value of the detached reference, used so the shadow
;; inputs show the resolved value of the copied fields.
detached* (mf/use-state nil)
detached (deref detached*)
composite-token
(mf/with-memo [token detached]
(if (some? detached)
(assoc token :value detached)
token))
on-detach
(mf/use-fn
(mf/deps form handle-toggle)
(fn [value]
(let [value (make-default-value value)]
(swap! form (fn [state]
(-> state
(assoc-in [:data :value] value)
(update :errors dissoc :value)
(update :extra-errors dissoc :value))))
(reset! detached* value)
(handle-toggle "composite"))))
on-add-shadow-block
(mf/use-fn
(mf/deps value-subfield)
@ -250,13 +317,14 @@
:id "reference-opt"}]]]
(if (= tab :composite)
[:> composite-form* {:token token
[:> composite-form* {:token composite-token
:tokens tokens
:remove-shadow-block remove-shadow-block
:value-subfield value-subfield}]
[:> reference-form* {:token token
:tokens tokens}])]))
:tokens tokens
:on-detach on-detach}])]))
;; TODO: use cfo/make-schema:token-value and extend it with shadow and reference fields
(defn- make-schema
@ -329,25 +397,6 @@
(or ref-valid? valid-composite-shadow?)))]]))
(defn- make-default-value
[value]
(cond
(string? value)
{:reference value
:shadow []}
(vector? value)
{:reference nil
:shadow (mapv (fn [shadow]
(-> shadow
(update :blur #(if (str/blank? %) "0" %))
(update :spread #(if (str/blank? %) "0" %))))
value)}
:else
{:reference nil
:shadow [default-token-shadow]}))
(mf/defc form*
[{:keys [token
token-type

View File

@ -32,6 +32,15 @@
padding-inline-start: var(--sp-m);
}
.detach-button {
visibility: hidden;
}
.reference-row:hover .detach-button,
.reference-row:focus-within .detach-button {
visibility: visible;
}
.title-bar {
display: grid;
grid-template-columns: 1fr auto auto;

View File

@ -13,10 +13,14 @@
[app.common.types.token :as cto]
[app.main.data.workspace.tokens.errors :as wte]
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.forms :as forms]
[app.main.ui.workspace.tokens.management.forms.controls :as token.controls]
[app.main.ui.workspace.tokens.management.forms.controls.utils :as csu]
[app.main.ui.workspace.tokens.management.forms.generic-form :as generic]
[app.main.ui.workspace.tokens.management.forms.validators :refer [check-coll-self-reference check-self-reference default-validate-token]]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
@ -60,6 +64,25 @@
check-typography-token-self-reference])
(default-validate-token))))
;; HELPERS
(def ^:private composite-keys
[:font-family
:font-size
:font-weight
:line-height
:letter-spacing
:text-case
:text-decoration])
(defn- composite-value->form-value
"Form data for the composite tab from a typography token map `value`."
[value]
(-> (cond-> value
(:font-family value)
(update :font-family cto/join-font-family))
(select-keys composite-keys)))
;; COMPONENTS
(mf/defc composite-form*
@ -172,40 +195,88 @@
:tokens tokens}]]]))
(mf/defc reference-form*
[{:keys [token tokens] :as props}]
[:div {:class (stl/css :input-row)}
[:> token.controls/input-composite*
{:placeholder (tr "workspace.tokens.reference-composite")
:aria-label (tr "labels.reference")
:icon i/text-typography
:name :reference
:token token
:tokens tokens}]])
[{:keys [token tokens on-detach] :as props}]
(let [form (mf/use-ctx forms/context)
reference (get-in @form [:data :value :reference])
detached-value
(mf/with-memo [tokens reference]
(csu/referenced-token-value tokens reference :typography))
on-detach-click
(mf/use-fn
(mf/deps detached-value on-detach)
(fn [event]
(dom/prevent-default event)
(on-detach detached-value)))]
[:div {:class (stl/css :input-row :reference-row)}
[:> token.controls/input-composite*
{:placeholder (tr "workspace.tokens.reference-composite")
:aria-label (tr "labels.reference")
:icon i/text-typography
:name :reference
:token token
:tokens tokens
:slot-end (when (some? detached-value)
(mf/html
[:> icon-button* {:variant "action"
:type "button"
:class (stl/css :detach-button)
:aria-label (tr "workspace.tokens.detach-reference")
:on-click on-detach-click
:icon i/detach}]))}]]))
(mf/defc tabs-wrapper*
[{:keys [token tokens tab handle-toggle] :rest props}]
[:*
[:div {:class (stl/css :title-bar)}
[:div {:class (stl/css :title)} (tr "labels.typography")]
[:& radio-buttons {:class (stl/css :listing-options)
:selected (d/name tab)
:on-change handle-toggle
:name "reference-composite-tab"}
[:& radio-button {:icon i/layers
:value "composite"
:title (tr "workspace.tokens.individual-tokens")
:id "composite-opt"}]
[:& radio-button {:icon i/tokens
:value "reference"
:title (tr "workspace.tokens.use-reference")
:id "reference-opt"}]]]
[:div {:class (stl/css :inputs-wrapper)}
(if (= tab :composite)
[:> composite-form* {:token token
:tokens tokens}]
(let [form (mf/use-ctx forms/context)
[:> reference-form* {:token token
:tokens tokens}])]])
;; Raw value of the detached reference, used so the composite
;; inputs show the resolved value of the copied fields.
detached* (mf/use-state nil)
detached (deref detached*)
composite-token
(mf/with-memo [token detached]
(if (some? detached)
(assoc token :value detached)
token))
on-detach
(mf/use-fn
(mf/deps form handle-toggle)
(fn [value]
(swap! form (fn [state]
(-> state
(assoc-in [:data :value] (composite-value->form-value value))
(update :errors dissoc :value)
(update :extra-errors dissoc :value))))
(reset! detached* value)
(handle-toggle "composite")))]
[:*
[:div {:class (stl/css :title-bar)}
[:div {:class (stl/css :title)} (tr "labels.typography")]
[:& radio-buttons {:class (stl/css :listing-options)
:selected (d/name tab)
:on-change handle-toggle
:name "reference-composite-tab"}
[:& radio-button {:icon i/layers
:value "composite"
:title (tr "workspace.tokens.individual-tokens")
:id "composite-opt"}]
[:& radio-button {:icon i/tokens
:value "reference"
:title (tr "workspace.tokens.use-reference")
:id "reference-opt"}]]]
[:div {:class (stl/css :inputs-wrapper)}
(if (= tab :composite)
[:> composite-form* {:token composite-token
:tokens tokens}]
[:> reference-form* {:token token
:tokens tokens
:on-detach on-detach}])]]))
;; SCHEMA
@ -274,17 +345,8 @@
{:reference value}
(map? value)
(let [value (cond-> value
(:font-family value)
(update :font-family cto/join-font-family))]
(select-keys value
[:font-family
:font-size
:font-weight
:line-height
:letter-spacing
:text-case
:text-decoration]))
(composite-value->form-value value)
:else
{})]

View File

@ -23,6 +23,15 @@
gap: var(--sp-xs);
}
.detach-button {
visibility: hidden;
}
.reference-row:hover .detach-button,
.reference-row:focus-within .detach-button {
visibility: visible;
}
.title-bar {
display: grid;
grid-template-columns: 1fr auto;

View File

@ -97,6 +97,7 @@
[frontend-tests.tokens.logic.token-data-test]
[frontend-tests.tokens.logic.token-remapping-test]
[frontend-tests.tokens.logic.tokens-status-test]
[frontend-tests.tokens.referenced-token-value-test]
[frontend-tests.tokens.style-dictionary-test]
[frontend-tests.tokens.token-errors-test]
[frontend-tests.tokens.workspace-tokens-remap-test]
@ -234,6 +235,7 @@
'frontend-tests.tokens.logic.token-actions-test
'frontend-tests.tokens.logic.token-data-test
'frontend-tests.tokens.logic.token-remapping-test
'frontend-tests.tokens.referenced-token-value-test
'frontend-tests.tokens.style-dictionary-test
'frontend-tests.tokens.token-errors-test
'frontend-tests.tokens.logic.tokens-status-test

View File

@ -0,0 +1,55 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.tokens.referenced-token-value-test
(:require
[app.main.ui.workspace.tokens.management.forms.controls.utils :as csu]
[cljs.test :as t :include-macros true]))
(def ^:private typography-value
{:font-family ["Inter"]
:font-size "16"
:font-weight "600"})
(def ^:private shadow-value
[{:offset-x "4" :offset-y "4" :blur "8" :spread "0" :color "#000000" :inset false}
{:offset-x "0" :offset-y "1" :blur "2" :spread "0" :color "#00000033" :inset true}])
(def ^:private tokens
{"base" {:name "base" :type :typography :value typography-value}
"alias" {:name "alias" :type :typography :value "{base}"}
"loop-a" {:name "loop-a" :type :typography :value "{loop-b}"}
"loop-b" {:name "loop-b" :type :typography :value "{loop-a}"}
"font-size" {:name "font-size" :type :font-size :value "16"}
"shadow-base" {:name "shadow-base" :type :shadow :value shadow-value}
"shadow-alias" {:name "shadow-alias" :type :shadow :value "{shadow-base}"}})
(t/deftest referenced-typography-value
(t/testing "returns the value of the referenced typography token"
(t/is (= typography-value (csu/referenced-token-value tokens "{base}" :typography)))
(t/is (= typography-value (csu/referenced-token-value tokens " {base} " :typography))))
(t/testing "follows chained references"
(t/is (= typography-value (csu/referenced-token-value tokens "{alias}" :typography))))
(t/testing "returns nil when the reference cannot be detached"
(t/is (nil? (csu/referenced-token-value tokens "{missing}" :typography)))
(t/is (nil? (csu/referenced-token-value tokens "{font-size}" :typography)))
(t/is (nil? (csu/referenced-token-value tokens "{loop-a}" :typography)))
(t/is (nil? (csu/referenced-token-value tokens "{base} {alias}" :typography)))
(t/is (nil? (csu/referenced-token-value tokens "base" :typography)))
(t/is (nil? (csu/referenced-token-value tokens nil :typography)))))
(t/deftest referenced-shadow-value
(t/testing "returns the value of the referenced shadow token"
(t/is (= shadow-value (csu/referenced-token-value tokens "{shadow-base}" :shadow))))
(t/testing "follows chained references"
(t/is (= shadow-value (csu/referenced-token-value tokens "{shadow-alias}" :shadow))))
(t/testing "returns nil for a token of another type"
(t/is (nil? (csu/referenced-token-value tokens "{base}" :shadow)))
(t/is (nil? (csu/referenced-token-value tokens "{shadow-base}" :typography)))))

View File

@ -3403,7 +3403,7 @@ msgstr "Themes"
msgid "labels.tutorials"
msgstr "Tutorials"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "labels.typography"
msgstr "Typography"
@ -8711,6 +8711,10 @@ msgstr "Delete token"
msgid "workspace.tokens.delete-theme-title"
msgstr "Delete theme"
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.detach-reference"
msgstr "Detach reference"
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:430
msgid "workspace.tokens.duplicate"
msgstr "Duplicate token"
@ -8771,7 +8775,7 @@ msgstr "Preview:"
msgid "workspace.tokens.export.single-file"
msgstr "Single file"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.font-size-value-enter"
msgstr "Font size or {alias}"
@ -8855,11 +8859,11 @@ msgstr "Theme"
msgid "workspace.tokens.label.theme-placeholder"
msgstr "Add a theme (i.e. Light)"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.letter-spacing-value-enter-composite"
msgstr "Letter spacing or {alias}"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.line-height-value-enter"
msgstr "Line height (multiplier, px, %) or {alias}"
@ -8951,7 +8955,7 @@ msgstr "Radius"
msgid "workspace.tokens.ref-not-valid"
msgstr "Reference in {%s} is not valid or is not in any active set."
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.reference-composite"
msgstr "Enter a token typography alias"
@ -9105,11 +9109,11 @@ msgstr "Description"
msgid "workspace.tokens.token-font-family-select"
msgstr "Select font family"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.token-font-family-value"
msgstr "Font family"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.token-font-family-value-enter"
msgstr "Font family or list of fonts separated by comma (,)"

View File

@ -3440,7 +3440,7 @@ msgstr "Temas"
msgid "labels.tutorials"
msgstr "Tutoriales"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "labels.typography"
msgstr "Tipografía"
@ -8783,6 +8783,10 @@ msgstr "Eliminar token"
msgid "workspace.tokens.delete-theme-title"
msgstr "Borrar theme"
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.detach-reference"
msgstr "Desvincular referencia"
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:430
msgid "workspace.tokens.duplicate"
msgstr "Duplicar token"
@ -8845,7 +8849,7 @@ msgstr "Previsualizar:"
msgid "workspace.tokens.export.single-file"
msgstr "fichero único"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.font-size-value-enter"
msgstr "Font size o {alias}"
@ -8933,11 +8937,11 @@ msgstr "Tema"
msgid "workspace.tokens.label.theme-placeholder"
msgstr "Añade un Tema (p. ej. Claro)"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.letter-spacing-value-enter-composite"
msgstr "Letter spacing o {alias}"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.line-height-value-enter"
msgstr "Line height (multiplicador, px o %) o {alias}"
@ -9031,7 +9035,7 @@ msgstr "Radio"
msgid "workspace.tokens.ref-not-valid"
msgstr "La referencia en {%s} no es válida o no se encuentra en ningún set activo."
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.reference-composite"
msgstr "Introduce un alias de tipografía de token"
@ -9187,11 +9191,11 @@ msgstr "Descripción"
msgid "workspace.tokens.token-font-family-select"
msgstr "Seleccionar familia tipográfica"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.token-font-family-value"
msgstr "Familia tipográfica"
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226
msgid "workspace.tokens.token-font-family-value-enter"
msgstr "Familia tipográfica o lista de fuentes separadas por coma (,)"