From 1b7023c67b07e0629b6d47e44fe72614f2a4999a Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 25 Aug 2026 03:42:04 +0300 Subject: [PATCH 01/20] feat(theme): expose web theme context --- ui-parts/theme/src/index.ts | 1 + ui-parts/theme/src/theme.context.tsx | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 ui-parts/theme/src/theme.context.tsx diff --git a/ui-parts/theme/src/index.ts b/ui-parts/theme/src/index.ts index 6ee249a95..0d07e6db7 100644 --- a/ui-parts/theme/src/index.ts +++ b/ui-parts/theme/src/index.ts @@ -1,3 +1,4 @@ export * from './properties/index.js' +export * from './theme.context.js' export * from './theme.css.js' export * from './utils/index.js' diff --git a/ui-parts/theme/src/theme.context.tsx b/ui-parts/theme/src/theme.context.tsx new file mode 100644 index 000000000..99ef02719 --- /dev/null +++ b/ui-parts/theme/src/theme.context.tsx @@ -0,0 +1,23 @@ +import type { Theme } from '@atls-ui/theme' +import type { PropsWithChildren } from 'react' +import type { ReactNode } from 'react' + +import { createContext } from 'react' +import { useContext } from 'react' + +import { lightTheme } from '@atls-ui/theme' + +export interface ThemeProviderProps { + theme?: Theme +} + +const ThemeContext = createContext(lightTheme) + +export const ThemeProvider = ({ + children, + theme = lightTheme, +}: PropsWithChildren): ReactNode => ( + {children} +) + +export const useTheme = (): Theme => useContext(ThemeContext) From 36acf2c9d17ff442fe8c739be4e4bead060a91ff Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 25 Aug 2026 03:46:14 +0300 Subject: [PATCH 02/20] feat(button): update web button --- .pnp.cjs | 74 ++------ ui-admin/button/src/styles/appearance.css.ts | 170 ++++++++---------- ui-parts/button/package.json | 3 +- ui-parts/button/src/button.component.tsx | 140 +++++++++++---- ui-parts/button/src/button.interfaces.ts | 12 +- .../ghost-button/ghost-button.component.tsx | 9 +- ui-parts/button/src/index.ts | 7 +- ui-parts/button/src/styles/appearance.css.ts | 157 +++++----------- ui-parts/button/src/styles/appearance.ts | 101 +++++++++++ ui-parts/button/src/styles/base.css.ts | 18 +- ui-parts/button/src/styles/constants.ts | 13 ++ ui-parts/button/src/styles/index.ts | 5 + ui-parts/button/src/styles/interfaces.ts | 52 +++++- ui-parts/button/src/styles/layout.css.ts | 65 +++++++ ui-parts/button/src/styles/schema.test.ts | 16 -- ui-parts/button/src/styles/schema.ts | 48 ----- ui-parts/button/src/styles/shape.css.ts | 126 ++++++------- ui-parts/button/src/styles/shape.ts | 91 ++++++++++ yarn.lock | 3 +- 19 files changed, 641 insertions(+), 469 deletions(-) create mode 100644 ui-parts/button/src/styles/appearance.ts create mode 100644 ui-parts/button/src/styles/constants.ts create mode 100644 ui-parts/button/src/styles/layout.css.ts delete mode 100644 ui-parts/button/src/styles/schema.test.ts delete mode 100644 ui-parts/button/src/styles/schema.ts create mode 100644 ui-parts/button/src/styles/shape.ts diff --git a/.pnp.cjs b/.pnp.cjs index dbaf5fb46..2af48fed5 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -249,7 +249,7 @@ const RAW_RUNTIME_STATE = ["@atls-ui-admin/text", ["workspace:ui-admin/text"]],\ ["@atls-ui-admin/theme", ["virtual:90c3fa37a29dbddd012ae896ef9d136c46ca1d320faf1baf430439ab5aac7e1e7c8ef5281a72424df8d016a8c33c9d5ffe66ee23d2face19f27de3042508900f#workspace:ui-admin/theme", "virtual:b32a26a2fdb9efe95b0d3c1680d4b91fdda6b5202d080748278d5d418cc8670dced3df8a9ed2862d4501349fbf99cee1cc30a7bdbef4b607caffd1b77c40d6b4#workspace:ui-admin/theme", "workspace:ui-admin/theme"]],\ ["@atls-ui-admin/upload", ["workspace:ui-admin/upload"]],\ - ["@atls-ui-generators/appearance", ["virtual:ab74b0ad21d788b5faaef36e69cfdc113b850f1834ee613c0e84e69c90b4422225458e49681dd5efa10b62df03744b2c5390da900b6092fa9e40fd6605cda940#workspace:generators/appearance", "virtual:dbe1a421eee484ea0bdb9d0589d44d9184c9819d8f5bfcb1a73e3d85271cc45ecf4365a26e00f2a3256c13de48b40272578801688978809175001fb2693bc747#workspace:generators/appearance", "workspace:generators/appearance"]],\ + ["@atls-ui-generators/appearance", ["virtual:8480592f2ac0c5b00b17bd33d76d0c6d79665a3657c712c5bc5180464a86ac9bcb81d64ef54341b94b31e43db66a2fad906cfc6036c573c170560d657b7f96aa#workspace:generators/appearance", "workspace:generators/appearance"]],\ ["@atls-ui-generators/components", ["workspace:generators/components"]],\ ["@atls-ui-generators/icons", ["workspace:generators/icons"]],\ ["@atls-ui-generators/locales", ["workspace:generators/locales"]],\ @@ -291,7 +291,7 @@ const RAW_RUNTIME_STATE = ["@atls-ui/design", ["workspace:ui/design"]],\ ["@atls-ui/theme", ["workspace:ui/theme"]],\ ["@atls-utils/use-float", ["virtual:e8b379d238bee0c446fdeaddb9725553fcee91eeb8db9f1f4b47e4e36a5c6c26c07ae1861b7d0a5dd574863bc008f048d25fc793392ceeaade0c9801b921d418#workspace:utils/use-float", "workspace:utils/use-float"]],\ - ["@atls-utils/use-hover", ["virtual:90c3fa37a29dbddd012ae896ef9d136c46ca1d320faf1baf430439ab5aac7e1e7c8ef5281a72424df8d016a8c33c9d5ffe66ee23d2face19f27de3042508900f#workspace:utils/use-hover", "virtual:ab74b0ad21d788b5faaef36e69cfdc113b850f1834ee613c0e84e69c90b4422225458e49681dd5efa10b62df03744b2c5390da900b6092fa9e40fd6605cda940#workspace:utils/use-hover", "workspace:utils/use-hover"]],\ + ["@atls-utils/use-hover", ["virtual:90c3fa37a29dbddd012ae896ef9d136c46ca1d320faf1baf430439ab5aac7e1e7c8ef5281a72424df8d016a8c33c9d5ffe66ee23d2face19f27de3042508900f#workspace:utils/use-hover", "workspace:utils/use-hover"]],\ ["@atls-utils/use-outside-click", ["workspace:utils/use-outside-click"]],\ ["@atls-utils/use-select", ["virtual:9c076913ffd643939b8ac6e8c374fd1470f44689001261d35898fb07091ea0aa596a24d8bcdfb508f949d0c68789b448702cfb68072c423229496303e9a11342#workspace:utils/use-select", "workspace:utils/use-select"]],\ ["hyperion", ["workspace:."]]\ @@ -738,42 +738,10 @@ const RAW_RUNTIME_STATE = }]\ ]],\ ["@atls-ui-generators/appearance", [\ - ["virtual:ab74b0ad21d788b5faaef36e69cfdc113b850f1834ee613c0e84e69c90b4422225458e49681dd5efa10b62df03744b2c5390da900b6092fa9e40fd6605cda940#workspace:generators/appearance", {\ - "packageLocation": "./.yarn/__virtual__/@atls-ui-generators-appearance-virtual-b2d4646deb/1/generators/appearance/",\ + ["virtual:8480592f2ac0c5b00b17bd33d76d0c6d79665a3657c712c5bc5180464a86ac9bcb81d64ef54341b94b31e43db66a2fad906cfc6036c573c170560d657b7f96aa#workspace:generators/appearance", {\ + "packageLocation": "./.yarn/__virtual__/@atls-ui-generators-appearance-virtual-c407542ddb/1/generators/appearance/",\ "packageDependencies": [\ - ["@atls-ui-generators/appearance", "virtual:ab74b0ad21d788b5faaef36e69cfdc113b850f1834ee613c0e84e69c90b4422225458e49681dd5efa10b62df03744b2c5390da900b6092fa9e40fd6605cda940#workspace:generators/appearance"],\ - ["@atls-ui-generators/utils", "workspace:generators/utils"],\ - ["@atls-ui-parts/theme", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#workspace:ui-parts/theme"],\ - ["@swc-node/register", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#npm:1.9.0"],\ - ["@swc/core", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#npm:1.6.1"],\ - ["@types/node", "npm:22.15.3"],\ - ["@types/react", "npm:19.1.2"],\ - ["@types/react-dom", "virtual:5d5a2d83c74dbbb6a29a65ad5d9fce09aed4258bbc7c7bef7d0ae41fe2cbed3efa30eb23487731e7ba540c2485008f2e2cdc8aa4b98cd0d0d6df800a358cdf39#npm:19.1.2"],\ - ["@types/vanilla-extract__css", null],\ - ["@types/vanilla-extract__dynamic", null],\ - ["@vanilla-extract/css", "npm:1.17.1"],\ - ["@vanilla-extract/dynamic", "npm:2.1.2"],\ - ["commander", "npm:13.1.0"],\ - ["react", "npm:19.1.0"],\ - ["react-dom", "virtual:76ebc481ac02aa95961a7136f7e551f7070fbf8eb171c0ca48399e504fe0f0f0edaf1d4f5953b763ae0b309e0e91ebf7d54e047cc0a0270d8aee71a756c50a70#npm:19.1.0"],\ - ["tsx", "npm:4.19.3"],\ - ["typescript", "patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=379a07"]\ - ],\ - "packagePeers": [\ - "@types/react-dom",\ - "@types/vanilla-extract__css",\ - "@types/vanilla-extract__dynamic",\ - "@vanilla-extract/css",\ - "@vanilla-extract/dynamic",\ - "react-dom",\ - "react"\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:dbe1a421eee484ea0bdb9d0589d44d9184c9819d8f5bfcb1a73e3d85271cc45ecf4365a26e00f2a3256c13de48b40272578801688978809175001fb2693bc747#workspace:generators/appearance", {\ - "packageLocation": "./.yarn/__virtual__/@atls-ui-generators-appearance-virtual-0ba6f9713b/1/generators/appearance/",\ - "packageDependencies": [\ - ["@atls-ui-generators/appearance", "virtual:dbe1a421eee484ea0bdb9d0589d44d9184c9819d8f5bfcb1a73e3d85271cc45ecf4365a26e00f2a3256c13de48b40272578801688978809175001fb2693bc747#workspace:generators/appearance"],\ + ["@atls-ui-generators/appearance", "virtual:8480592f2ac0c5b00b17bd33d76d0c6d79665a3657c712c5bc5180464a86ac9bcb81d64ef54341b94b31e43db66a2fad906cfc6036c573c170560d657b7f96aa#workspace:generators/appearance"],\ ["@atls-ui-generators/utils", "workspace:generators/utils"],\ ["@atls-ui-parts/theme", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#workspace:ui-parts/theme"],\ ["@swc-node/register", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#npm:1.9.0"],\ @@ -935,11 +903,10 @@ const RAW_RUNTIME_STATE = ["virtual:90c3fa37a29dbddd012ae896ef9d136c46ca1d320faf1baf430439ab5aac7e1e7c8ef5281a72424df8d016a8c33c9d5ffe66ee23d2face19f27de3042508900f#workspace:ui-parts/button", {\ "packageLocation": "./.yarn/__virtual__/@atls-ui-parts-button-virtual-dbe1a421ee/1/ui-parts/button/",\ "packageDependencies": [\ - ["@atls-ui-generators/appearance", "virtual:dbe1a421eee484ea0bdb9d0589d44d9184c9819d8f5bfcb1a73e3d85271cc45ecf4365a26e00f2a3256c13de48b40272578801688978809175001fb2693bc747#workspace:generators/appearance"],\ ["@atls-ui-parts/button", "virtual:90c3fa37a29dbddd012ae896ef9d136c46ca1d320faf1baf430439ab5aac7e1e7c8ef5281a72424df8d016a8c33c9d5ffe66ee23d2face19f27de3042508900f#workspace:ui-parts/button"],\ ["@atls-ui-parts/layout", "virtual:76ebc481ac02aa95961a7136f7e551f7070fbf8eb171c0ca48399e504fe0f0f0edaf1d4f5953b763ae0b309e0e91ebf7d54e047cc0a0270d8aee71a756c50a70#workspace:ui-parts/layout"],\ ["@atls-ui-parts/theme", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#workspace:ui-parts/theme"],\ - ["@atls-utils/use-hover", "virtual:90c3fa37a29dbddd012ae896ef9d136c46ca1d320faf1baf430439ab5aac7e1e7c8ef5281a72424df8d016a8c33c9d5ffe66ee23d2face19f27de3042508900f#workspace:utils/use-hover"],\ + ["@atls-ui/theme", "workspace:ui/theme"],\ ["@storybook/react", "virtual:76ebc481ac02aa95961a7136f7e551f7070fbf8eb171c0ca48399e504fe0f0f0edaf1d4f5953b763ae0b309e0e91ebf7d54e047cc0a0270d8aee71a756c50a70#npm:10.4.6"],\ ["@types/react", "npm:19.1.2"],\ ["@types/react-dom", null],\ @@ -968,11 +935,10 @@ const RAW_RUNTIME_STATE = ["virtual:937fb0364248873ae59b9853300486d03005abda92eaabc1af692cdbfe45a10bb44400f85b426164577e95380d1eb384e54a3525e8f98eff7f80d492dbab08a5#workspace:ui-parts/button", {\ "packageLocation": "./.yarn/__virtual__/@atls-ui-parts-button-virtual-ab74b0ad21/1/ui-parts/button/",\ "packageDependencies": [\ - ["@atls-ui-generators/appearance", "virtual:ab74b0ad21d788b5faaef36e69cfdc113b850f1834ee613c0e84e69c90b4422225458e49681dd5efa10b62df03744b2c5390da900b6092fa9e40fd6605cda940#workspace:generators/appearance"],\ ["@atls-ui-parts/button", "virtual:937fb0364248873ae59b9853300486d03005abda92eaabc1af692cdbfe45a10bb44400f85b426164577e95380d1eb384e54a3525e8f98eff7f80d492dbab08a5#workspace:ui-parts/button"],\ ["@atls-ui-parts/layout", "virtual:937fb0364248873ae59b9853300486d03005abda92eaabc1af692cdbfe45a10bb44400f85b426164577e95380d1eb384e54a3525e8f98eff7f80d492dbab08a5#workspace:ui-parts/layout"],\ ["@atls-ui-parts/theme", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#workspace:ui-parts/theme"],\ - ["@atls-utils/use-hover", "virtual:ab74b0ad21d788b5faaef36e69cfdc113b850f1834ee613c0e84e69c90b4422225458e49681dd5efa10b62df03744b2c5390da900b6092fa9e40fd6605cda940#workspace:utils/use-hover"],\ + ["@atls-ui/theme", "workspace:ui/theme"],\ ["@storybook/react", "virtual:5d5a2d83c74dbbb6a29a65ad5d9fce09aed4258bbc7c7bef7d0ae41fe2cbed3efa30eb23487731e7ba540c2485008f2e2cdc8aa4b98cd0d0d6df800a358cdf39#npm:10.4.6"],\ ["@types/react", "npm:19.1.2"],\ ["@types/react-dom", "virtual:5d5a2d83c74dbbb6a29a65ad5d9fce09aed4258bbc7c7bef7d0ae41fe2cbed3efa30eb23487731e7ba540c2485008f2e2cdc8aa4b98cd0d0d6df800a358cdf39#npm:19.1.2"],\ @@ -1001,11 +967,10 @@ const RAW_RUNTIME_STATE = ["workspace:ui-parts/button", {\ "packageLocation": "./ui-parts/button/",\ "packageDependencies": [\ - ["@atls-ui-generators/appearance", "virtual:dbe1a421eee484ea0bdb9d0589d44d9184c9819d8f5bfcb1a73e3d85271cc45ecf4365a26e00f2a3256c13de48b40272578801688978809175001fb2693bc747#workspace:generators/appearance"],\ ["@atls-ui-parts/button", "workspace:ui-parts/button"],\ ["@atls-ui-parts/layout", "virtual:76ebc481ac02aa95961a7136f7e551f7070fbf8eb171c0ca48399e504fe0f0f0edaf1d4f5953b763ae0b309e0e91ebf7d54e047cc0a0270d8aee71a756c50a70#workspace:ui-parts/layout"],\ ["@atls-ui-parts/theme", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#workspace:ui-parts/theme"],\ - ["@atls-utils/use-hover", "virtual:90c3fa37a29dbddd012ae896ef9d136c46ca1d320faf1baf430439ab5aac7e1e7c8ef5281a72424df8d016a8c33c9d5ffe66ee23d2face19f27de3042508900f#workspace:utils/use-hover"],\ + ["@atls-ui/theme", "workspace:ui/theme"],\ ["@storybook/react", "virtual:76ebc481ac02aa95961a7136f7e551f7070fbf8eb171c0ca48399e504fe0f0f0edaf1d4f5953b763ae0b309e0e91ebf7d54e047cc0a0270d8aee71a756c50a70#npm:10.4.6"],\ ["@types/react", "npm:19.1.2"],\ ["@vanilla-extract/css", "npm:1.17.1"],\ @@ -1072,7 +1037,7 @@ const RAW_RUNTIME_STATE = ["workspace:ui-parts/checkbox", {\ "packageLocation": "./ui-parts/checkbox/",\ "packageDependencies": [\ - ["@atls-ui-generators/appearance", "virtual:dbe1a421eee484ea0bdb9d0589d44d9184c9819d8f5bfcb1a73e3d85271cc45ecf4365a26e00f2a3256c13de48b40272578801688978809175001fb2693bc747#workspace:generators/appearance"],\ + ["@atls-ui-generators/appearance", "virtual:8480592f2ac0c5b00b17bd33d76d0c6d79665a3657c712c5bc5180464a86ac9bcb81d64ef54341b94b31e43db66a2fad906cfc6036c573c170560d657b7f96aa#workspace:generators/appearance"],\ ["@atls-ui-parts/checkbox", "workspace:ui-parts/checkbox"],\ ["@atls-ui-parts/layout", "virtual:76ebc481ac02aa95961a7136f7e551f7070fbf8eb171c0ca48399e504fe0f0f0edaf1d4f5953b763ae0b309e0e91ebf7d54e047cc0a0270d8aee71a756c50a70#workspace:ui-parts/layout"],\ ["@atls-ui-parts/theme", "virtual:6e3d13d4bb141546c851eafb803659d9bef8a8a4b826ab761e48399dcbf8cda80227a38097da69531ddae63c1380c43a5fad649d9ff8e485d2bc4c96720b9bd0#workspace:ui-parts/theme"],\ @@ -1405,7 +1370,7 @@ const RAW_RUNTIME_STATE = ["virtual:e8b379d238bee0c446fdeaddb9725553fcee91eeb8db9f1f4b47e4e36a5c6c26c07ae1861b7d0a5dd574863bc008f048d25fc793392ceeaade0c9801b921d418#workspace:ui-parts/input", {\ "packageLocation": "./.yarn/__virtual__/@atls-ui-parts-input-virtual-8480592f2a/1/ui-parts/input/",\ "packageDependencies": [\ - ["@atls-ui-generators/appearance", "virtual:dbe1a421eee484ea0bdb9d0589d44d9184c9819d8f5bfcb1a73e3d85271cc45ecf4365a26e00f2a3256c13de48b40272578801688978809175001fb2693bc747#workspace:generators/appearance"],\ + ["@atls-ui-generators/appearance", "virtual:8480592f2ac0c5b00b17bd33d76d0c6d79665a3657c712c5bc5180464a86ac9bcb81d64ef54341b94b31e43db66a2fad906cfc6036c573c170560d657b7f96aa#workspace:generators/appearance"],\ ["@atls-ui-parts/condition", "virtual:06315aec6049f1c6c32004d6e584058ffedd6c8eaf08aaee2ce03aaedeaf84c340968f24d92b3566001c478f3ac64eca59adabd63e33390cb5990761978a1ea2#workspace:ui-parts/condition"],\ ["@atls-ui-parts/events-state", "virtual:8480592f2ac0c5b00b17bd33d76d0c6d79665a3657c712c5bc5180464a86ac9bcb81d64ef54341b94b31e43db66a2fad906cfc6036c573c170560d657b7f96aa#workspace:ui-parts/events-state"],\ ["@atls-ui-parts/input", "virtual:e8b379d238bee0c446fdeaddb9725553fcee91eeb8db9f1f4b47e4e36a5c6c26c07ae1861b7d0a5dd574863bc008f048d25fc793392ceeaade0c9801b921d418#workspace:ui-parts/input"],\ @@ -1440,7 +1405,7 @@ const RAW_RUNTIME_STATE = ["workspace:ui-parts/input", {\ "packageLocation": "./ui-parts/input/",\ "packageDependencies": [\ - ["@atls-ui-generators/appearance", "virtual:dbe1a421eee484ea0bdb9d0589d44d9184c9819d8f5bfcb1a73e3d85271cc45ecf4365a26e00f2a3256c13de48b40272578801688978809175001fb2693bc747#workspace:generators/appearance"],\ + ["@atls-ui-generators/appearance", "virtual:8480592f2ac0c5b00b17bd33d76d0c6d79665a3657c712c5bc5180464a86ac9bcb81d64ef54341b94b31e43db66a2fad906cfc6036c573c170560d657b7f96aa#workspace:generators/appearance"],\ ["@atls-ui-parts/condition", "virtual:06315aec6049f1c6c32004d6e584058ffedd6c8eaf08aaee2ce03aaedeaf84c340968f24d92b3566001c478f3ac64eca59adabd63e33390cb5990761978a1ea2#workspace:ui-parts/condition"],\ ["@atls-ui-parts/events-state", "virtual:8480592f2ac0c5b00b17bd33d76d0c6d79665a3657c712c5bc5180464a86ac9bcb81d64ef54341b94b31e43db66a2fad906cfc6036c573c170560d657b7f96aa#workspace:ui-parts/events-state"],\ ["@atls-ui-parts/input", "workspace:ui-parts/input"],\ @@ -2236,23 +2201,6 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "SOFT"\ }],\ - ["virtual:ab74b0ad21d788b5faaef36e69cfdc113b850f1834ee613c0e84e69c90b4422225458e49681dd5efa10b62df03744b2c5390da900b6092fa9e40fd6605cda940#workspace:utils/use-hover", {\ - "packageLocation": "./.yarn/__virtual__/@atls-utils-use-hover-virtual-08c756f4b1/1/utils/use-hover/",\ - "packageDependencies": [\ - ["@atls-utils/use-hover", "virtual:ab74b0ad21d788b5faaef36e69cfdc113b850f1834ee613c0e84e69c90b4422225458e49681dd5efa10b62df03744b2c5390da900b6092fa9e40fd6605cda940#workspace:utils/use-hover"],\ - ["@types/node", "npm:22.15.3"],\ - ["@types/react", "npm:19.1.2"],\ - ["@types/react-dom", "virtual:5d5a2d83c74dbbb6a29a65ad5d9fce09aed4258bbc7c7bef7d0ae41fe2cbed3efa30eb23487731e7ba540c2485008f2e2cdc8aa4b98cd0d0d6df800a358cdf39#npm:19.1.2"],\ - ["react", "npm:19.1.0"],\ - ["react-dom", "virtual:76ebc481ac02aa95961a7136f7e551f7070fbf8eb171c0ca48399e504fe0f0f0edaf1d4f5953b763ae0b309e0e91ebf7d54e047cc0a0270d8aee71a756c50a70#npm:19.1.0"]\ - ],\ - "packagePeers": [\ - "@types/react-dom",\ - "react-dom",\ - "react"\ - ],\ - "linkType": "SOFT"\ - }],\ ["workspace:utils/use-hover", {\ "packageLocation": "./utils/use-hover/",\ "packageDependencies": [\ diff --git a/ui-admin/button/src/styles/appearance.css.ts b/ui-admin/button/src/styles/appearance.css.ts index ed3e0c043..b36f80893 100644 --- a/ui-admin/button/src/styles/appearance.css.ts +++ b/ui-admin/button/src/styles/appearance.css.ts @@ -1,103 +1,87 @@ -import type { ButtonAppearance } from '@atls-ui-parts/button' +import type { ButtonAppearance } from '@atls-ui-parts/button' -import { vars } from '@atls-ui-admin/theme' -import { createAppearanceStyles } from '@atls-ui-parts/button' - -const appearanceBlueDefaultStyles = createAppearanceStyles({ - fontColor: vars.colors['button.blue.default.font'], - backgroundColor: vars.colors['button.blue.default.background'], - borderColor: vars.colors['button.blue.default.border'], -}) - -const appearanceBlueHoverStyles = createAppearanceStyles({ - fontColor: vars.colors['button.blue.hover.font'], - backgroundColor: vars.colors['button.blue.hover.background'], - borderColor: vars.colors['button.blue.hover.border'], -}) - -const appearanceBluePressedStyles = createAppearanceStyles({ - fontColor: vars.colors['button.blue.pressed.font'], - backgroundColor: vars.colors['button.blue.pressed.background'], - borderColor: vars.colors['button.blue.pressed.border'], -}) - -const appearanceBlueDisabledStyles = createAppearanceStyles({ - fontColor: vars.colors['button.blue.disabled.font'], - backgroundColor: vars.colors['button.blue.disabled.background'], - borderColor: vars.colors['button.blue.disabled.border'], -}) - -const appearanceLightBlueDefaultStyles = createAppearanceStyles({ - fontColor: vars.colors['button.lightBlue.default.font'], - backgroundColor: vars.colors['button.lightBlue.default.background'], - borderColor: vars.colors['button.lightBlue.default.border'], -}) - -const appearanceLightBlueHoverStyles = createAppearanceStyles({ - fontColor: vars.colors['button.lightBlue.hover.font'], - backgroundColor: vars.colors['button.lightBlue.hover.background'], - borderColor: vars.colors['button.lightBlue.hover.border'], -}) - -const appearanceLightBluePressedStyles = createAppearanceStyles({ - fontColor: vars.colors['button.lightBlue.pressed.font'], - backgroundColor: vars.colors['button.lightBlue.pressed.background'], - borderColor: vars.colors['button.lightBlue.pressed.border'], -}) - -const appearanceLightBlueDisabledStyles = createAppearanceStyles({ - fontColor: vars.colors['button.lightBlue.disabled.font'], - backgroundColor: vars.colors['button.lightBlue.disabled.background'], - borderColor: vars.colors['button.lightBlue.disabled.border'], -}) - -const appearanceGhostStyles = createAppearanceStyles({ - fontColor: 'inherit', - backgroundColor: 'transparent', - border: 'none', -}) - -export const appearanceVariant = { - blue: appearanceBlueDefaultStyles, - lightBlue: appearanceLightBlueDefaultStyles, - ghost: appearanceGhostStyles, -} - -export const appearanceHover = { - blueHover: appearanceBlueHoverStyles, - lightBlueHover: appearanceLightBlueHoverStyles, - ghostHover: appearanceGhostStyles, -} - -export const appearancePressed = { - bluePressed: appearanceBluePressedStyles, - lightBluePressed: appearanceLightBluePressedStyles, - ghostPressed: appearanceGhostStyles, -} - -export const appearanceDisabled = { - blueDisabled: appearanceBlueDisabledStyles, - lightBlueDisabled: appearanceLightBlueDisabledStyles, - ghostDisabled: appearanceGhostStyles, -} +import { vars } from '@atls-ui-admin/theme' export const buttonAppearances: Record<'blue' | 'ghost' | 'lightBlue', ButtonAppearance> = { blue: { - default: appearanceVariant.blue, - hover: appearanceHover.blueHover, - pressed: appearancePressed.bluePressed, - disabled: appearanceDisabled.blueDisabled, + default: { + background: vars.colors['button.blue.default.background'], + content: vars.colors['button.blue.default.font'], + border: vars.colors['button.blue.default.border'], + }, + hover: { + background: vars.colors['button.blue.hover.background'], + content: vars.colors['button.blue.hover.font'], + border: vars.colors['button.blue.hover.border'], + }, + pressed: { + background: vars.colors['button.blue.pressed.background'], + content: vars.colors['button.blue.pressed.font'], + border: vars.colors['button.blue.pressed.border'], + }, + disabled: { + background: vars.colors['button.blue.disabled.background'], + content: vars.colors['button.blue.disabled.font'], + border: vars.colors['button.blue.disabled.border'], + }, + focused: { + background: vars.colors['button.blue.default.background'], + content: vars.colors['button.blue.default.font'], + border: vars.colors['button.blue.default.border'], + }, }, lightBlue: { - default: appearanceVariant.lightBlue, - hover: appearanceHover.lightBlueHover, - pressed: appearancePressed.lightBluePressed, - disabled: appearanceDisabled.lightBlueDisabled, + default: { + background: vars.colors['button.lightBlue.default.background'], + content: vars.colors['button.lightBlue.default.font'], + border: vars.colors['button.lightBlue.default.border'], + }, + hover: { + background: vars.colors['button.lightBlue.hover.background'], + content: vars.colors['button.lightBlue.hover.font'], + border: vars.colors['button.lightBlue.hover.border'], + }, + pressed: { + background: vars.colors['button.lightBlue.pressed.background'], + content: vars.colors['button.lightBlue.pressed.font'], + border: vars.colors['button.lightBlue.pressed.border'], + }, + disabled: { + background: vars.colors['button.lightBlue.disabled.background'], + content: vars.colors['button.lightBlue.disabled.font'], + border: vars.colors['button.lightBlue.disabled.border'], + }, + focused: { + background: vars.colors['button.lightBlue.default.background'], + content: vars.colors['button.lightBlue.default.font'], + border: vars.colors['button.lightBlue.default.border'], + }, }, ghost: { - default: appearanceVariant.ghost, - hover: appearanceHover.ghostHover, - pressed: appearancePressed.ghostPressed, - disabled: appearanceDisabled.ghostDisabled, + default: { + background: 'transparent', + content: 'inherit', + border: 'transparent', + }, + hover: { + background: 'transparent', + content: 'inherit', + border: 'transparent', + }, + pressed: { + background: 'transparent', + content: 'inherit', + border: 'transparent', + }, + disabled: { + background: 'transparent', + content: 'inherit', + border: 'transparent', + }, + focused: { + background: 'transparent', + content: 'inherit', + border: 'transparent', + }, }, } diff --git a/ui-parts/button/package.json b/ui-parts/button/package.json index 8d6fe5cd1..0d327db9d 100644 --- a/ui-parts/button/package.json +++ b/ui-parts/button/package.json @@ -14,12 +14,11 @@ }, "dependencies": { "@atls-ui-parts/theme": "workspace:*", - "@atls-utils/use-hover": "workspace:*", + "@atls-ui/theme": "workspace:*", "clsx": "2.1.1", "rainbow-sprinkles": "1.0.0" }, "devDependencies": { - "@atls-ui-generators/appearance": "workspace:*", "@atls-ui-parts/layout": "workspace:*", "@storybook/react": "10.4.6", "@types/react": "19.1.2", diff --git a/ui-parts/button/src/button.component.tsx b/ui-parts/button/src/button.component.tsx index 8793fcc96..31c12e8ef 100644 --- a/ui-parts/button/src/button.component.tsx +++ b/ui-parts/button/src/button.component.tsx @@ -1,59 +1,129 @@ -import type { ReactNode } from 'react' +import type { CSSProperties } from 'react' +import type { ReactNode } from 'react' -import type { ButtonProps } from './button.interfaces.js' +import type { ButtonProps } from './button.interfaces.js' +import type { ButtonAppearanceStates } from './styles/interfaces.js' +import type { ButtonShapeProperties } from './styles/interfaces.js' -import { useState } from 'react' +import { assignInlineVars } from '@vanilla-extract/dynamic' +import { Children } from 'react' +import { clsx } from 'clsx' -import { useHover } from '@atls-utils/use-hover' +import { useTheme } from '@atls-ui-parts/theme' -import { buttonAppearances } from './styles/appearance.css.js' -import { baseStyles } from './styles/base.css.js' -import { buttonShapes } from './styles/shape.css.js' +import { appearanceStyles } from './styles/appearance.css.js' +import { appearanceVariables } from './styles/appearance.css.js' +import { buttonAppearances } from './styles/appearance.js' +import { resolveButtonAppearance } from './styles/appearance.js' +import { baseStyles } from './styles/base.css.js' +import { addonStyles } from './styles/layout.css.js' +import { addonsStyles } from './styles/layout.css.js' +import { contentStyles } from './styles/layout.css.js' +import { fullWidthContentStyles } from './styles/layout.css.js' +import { fullWidthStyles } from './styles/layout.css.js' +import { labelStyles } from './styles/layout.css.js' +import { layoutVariables } from './styles/layout.css.js' +import { leadingAddonStyles } from './styles/layout.css.js' +import { trailingAddonStyles } from './styles/layout.css.js' +import { shapeStyles } from './styles/shape.css.js' +import { shapeVariables } from './styles/shape.css.js' +import { buttonShapes } from './styles/shape.js' +import { resolveButtonShape } from './styles/shape.js' + +const addonSideWidth = (addonCount: number): string => { + if (addonCount === 0) { + return '0px' + } + + return `calc(${[ + ...Array.from({ length: addonCount }, () => shapeVariables.addonSize), + ...Array.from({ length: addonCount - 1 }, () => shapeVariables.gap), + ].join(' + ')})` +} + +const buttonInlineStyles = ( + appearance: ButtonAppearanceStates, + shape: ButtonShapeProperties, + addonCount: number, + style?: CSSProperties +): CSSProperties => ({ + ...assignInlineVars(appearanceVariables, appearance), + ...assignInlineVars(shapeVariables, { + ...shape, + typography: { + fontFamily: String(shape.typography.fontFamily ?? 'inherit'), + fontSize: String(shape.typography.fontSize ?? 'inherit'), + fontWeight: String(shape.typography.fontWeight ?? 'inherit'), + letterSpacing: String(shape.typography.letterSpacing ?? 'inherit'), + lineHeight: String(shape.typography.lineHeight ?? 'inherit'), + }, + }), + ...assignInlineVars(layoutVariables, { + sideGap: addonCount > 0 ? shapeVariables.gap : '0px', + sideWidth: addonSideWidth(addonCount), + }), + ...style, +}) export const Button = ({ children, ref, - icon, - iconPlacement, - appearance = buttonAppearances.blue, + leadingAddon, + trailingAddon, + appearance = buttonAppearances.primary, className, disabled, - shape = buttonShapes.huge, + fullWidth = false, + shape = buttonShapes.md, + style, ...props }: ButtonProps): ReactNode => { - const [pressed, setPressed] = useState(false) - const [hover, hoverProps] = useHover() - - const onMouseDown = (): void => { - setPressed(true) - } - - const onMouseUp = (): void => { - setPressed(false) - } + const theme = useTheme() + const resolvedAppearance = resolveButtonAppearance(appearance, theme) + const resolvedShape = resolveButtonShape(shape, theme) + const leadingAddons = Children.toArray(leadingAddon) + const trailingAddons = Children.toArray(trailingAddon) + const addonCount = Math.max(leadingAddons.length, trailingAddons.length) return ( ) } diff --git a/ui-parts/button/src/button.interfaces.ts b/ui-parts/button/src/button.interfaces.ts index fa339fc6f..1b3047928 100644 --- a/ui-parts/button/src/button.interfaces.ts +++ b/ui-parts/button/src/button.interfaces.ts @@ -1,19 +1,15 @@ import type { ButtonHTMLAttributes } from 'react' +import type { ReactNode } from 'react' import type { Ref } from 'react' -import type { JSX } from 'react' import type { ButtonAppearance } from './styles/interfaces.js' import type { ButtonShape } from './styles/interfaces.js' -export enum IconPlacement { - LEFT = 'Left', - RIGHT = 'Right', -} - export interface ButtonProps extends ButtonHTMLAttributes { appearance?: ButtonAppearance - icon?: JSX.Element - iconPlacement?: IconPlacement + fullWidth?: boolean + leadingAddon?: ReactNode ref?: Ref shape?: ButtonShape + trailingAddon?: ReactNode } diff --git a/ui-parts/button/src/ghost-button/ghost-button.component.tsx b/ui-parts/button/src/ghost-button/ghost-button.component.tsx index 249c1cece..94a5c3ccb 100644 --- a/ui-parts/button/src/ghost-button/ghost-button.component.tsx +++ b/ui-parts/button/src/ghost-button/ghost-button.component.tsx @@ -3,14 +3,11 @@ import type { ReactNode } from 'react' import type { ButtonProps } from '../button.interfaces.js' import { Button } from '../button.component.js' -import { buttonAppearances } from '../styles/appearance.css.js' -import { buttonShapes } from '../styles/shape.css.js' +import { buttonAppearances } from '../styles/appearance.js' +import { buttonShapes } from '../styles/shape.js' -/** - * @deprecated Use Button with buttonAppearances.ghost and buttonShapes.ghost instead. - */ export const GhostButton = ({ appearance = buttonAppearances.ghost, - shape = buttonShapes.ghost, + shape = buttonShapes.md, ...props }: ButtonProps): ReactNode => - ) -} diff --git a/ui-parts/button/src/component.tsx b/ui-parts/button/src/component.tsx new file mode 100644 index 000000000..ce0dd2d0d --- /dev/null +++ b/ui-parts/button/src/component.tsx @@ -0,0 +1,65 @@ +import type { ReactNode } from 'react' + +import type { ButtonProps } from './interfaces.js' + +import { clsx } from 'clsx' + +import { useTheme } from '@atls-ui-parts/theme' + +import { Addons } from './addons.js' +import { appearanceStyles } from './styles/index.js' +import { assignButtonVariables } from './styles/index.js' +import { baseStyles } from './styles/index.js' +import { buttonAppearances } from './styles/index.js' +import { buttonShapes } from './styles/index.js' +import { contentStyles } from './styles/index.js' +import { fullWidthContentStyles } from './styles/index.js' +import { fullWidthStyles } from './styles/index.js' +import { isButtonShapeName } from './styles/index.js' +import { labelStyles } from './styles/index.js' +import { resolveButtonAppearance } from './styles/index.js' +import { resolveButtonShape } from './styles/index.js' +import { shapeStyles } from './styles/index.js' + +export const Button = ({ + appearance = buttonAppearances.primary, + children, + className, + fullWidth = false, + leadingAddon, + shape = buttonShapes.md, + style, + trailingAddon, + ...props +}: ButtonProps): ReactNode => { + const theme = useTheme() + const resolvedAppearance = resolveButtonAppearance(appearance, theme) + const resolvedShape = resolveButtonShape(shape, theme) + const shapeClassName = typeof shape === 'string' && !isButtonShapeName(shape) ? shape : undefined + + return ( + + ) +} diff --git a/ui-parts/button/src/content/content/content.component.tsx b/ui-parts/button/src/content/content/content.component.tsx deleted file mode 100644 index 14d72b881..000000000 --- a/ui-parts/button/src/content/content/content.component.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { ReactNode } from 'react' -import type { ReactElement } from 'react' - -import type { ContentProps } from './content.interfaces.js' - -import { Divider } from '../divider/index.js' -import { useDividedChildren } from '../hooks/index.js' - -export const Content = ({ divider = 12, children }: ContentProps): ReactNode => - useDividedChildren(children, Divider, { divider }) as ReactElement diff --git a/ui-parts/button/src/content/content/content.interfaces.ts b/ui-parts/button/src/content/content/content.interfaces.ts deleted file mode 100644 index debc8f57c..000000000 --- a/ui-parts/button/src/content/content/content.interfaces.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { PropsWithChildren } from 'react' - -export interface ContentProps extends PropsWithChildren { - divider?: number -} diff --git a/ui-parts/button/src/content/content/index.ts b/ui-parts/button/src/content/content/index.ts deleted file mode 100644 index b7828ab6e..000000000 --- a/ui-parts/button/src/content/content/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './content.component.js' -export type * from './content.interfaces.js' diff --git a/ui-parts/button/src/content/divider/divider.component.tsx b/ui-parts/button/src/content/divider/divider.component.tsx deleted file mode 100644 index a6adea5cf..000000000 --- a/ui-parts/button/src/content/divider/divider.component.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import type { ReactNode } from 'react' - -import type { DividerProps } from './divider.interfaces.js' - -import { clsx } from 'clsx' - -import { dividerSprinkles } from './divider.css.js' - -export const Divider = ({ ref, ...props }: DividerProps): ReactNode => { - const { className, style, otherProps } = dividerSprinkles(props) - - return ( - - ) -} diff --git a/ui-parts/button/src/content/divider/divider.css.ts b/ui-parts/button/src/content/divider/divider.css.ts deleted file mode 100644 index 82b6f1931..000000000 --- a/ui-parts/button/src/content/divider/divider.css.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { SprinklesFn } from 'rainbow-sprinkles' - -import { style } from '@vanilla-extract/css' -import { createRainbowSprinkles } from 'rainbow-sprinkles' -import { defineProperties } from 'rainbow-sprinkles' - -export const dividerStyles = style({ - display: 'flex', -}) - -const dividerProperties = defineProperties({ - dynamicProperties: { - marginLeft: true, - }, - shorthands: { - divider: ['marginLeft'], - }, -}) - -export const dividerSprinkles: SprinklesFn<[typeof dividerProperties]> = - createRainbowSprinkles(dividerProperties) - -export type DividerSprinkles = Parameters[0] diff --git a/ui-parts/button/src/content/divider/divider.interfaces.ts b/ui-parts/button/src/content/divider/divider.interfaces.ts deleted file mode 100644 index a8927ee9d..000000000 --- a/ui-parts/button/src/content/divider/divider.interfaces.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { HTMLAttributes } from 'react' -import type { Ref } from 'react' - -import type { DividerSprinkles } from './divider.css.js' - -export interface DividerProps extends HTMLAttributes, DividerSprinkles { - ref?: Ref -} diff --git a/ui-parts/button/src/content/divider/index.ts b/ui-parts/button/src/content/divider/index.ts deleted file mode 100644 index e2036c205..000000000 --- a/ui-parts/button/src/content/divider/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './divider.component.js' diff --git a/ui-parts/button/src/content/hooks/index.ts b/ui-parts/button/src/content/hooks/index.ts deleted file mode 100644 index b7d7c1e84..000000000 --- a/ui-parts/button/src/content/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './use-divided-children.hook.js' diff --git a/ui-parts/button/src/content/hooks/use-divided-children.hook.ts b/ui-parts/button/src/content/hooks/use-divided-children.hook.ts deleted file mode 100644 index e399105d5..000000000 --- a/ui-parts/button/src/content/hooks/use-divided-children.hook.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { ReactNode } from 'react' - -import { Children } from 'react' -import { createElement } from 'react' - -const combineChildren = (children: ReactNode): Array => { - let isPrevChildPure: boolean = false - - const childList: Array = [] - - Children.forEach(children, (child) => { - const isCurrentChildPure = typeof child === 'string' || typeof child === 'number' - - if (isPrevChildPure && isCurrentChildPure) { - const lastIndex = childList.length - 1 - const lastChild = childList[lastIndex] - - // eslint-disable-next-line @typescript-eslint/no-base-to-string - childList[lastIndex] = [lastChild, child].join(' ') - } else { - childList.push(child) - } - - isPrevChildPure = isCurrentChildPure - }) - - return childList -} - -export const useDividedChildren = ( - children: ReactNode, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - Wrapper: (props: Record) => ReactNode, - props = {} -): Array | ReactNode => { - if (Children.count(children) > 1) { - const combinedChildren = combineChildren(children) - - return Children.map(combinedChildren, async (child, index) => { - if (index > 0) { - return createElement(Wrapper, props, child) - } - - return child - }) - } - - return children -} diff --git a/ui-parts/button/src/content/index.ts b/ui-parts/button/src/content/index.ts deleted file mode 100644 index 793fe0891..000000000 --- a/ui-parts/button/src/content/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './divider/index.js' -export * from './hooks/index.js' -export * from './content/index.js' diff --git a/ui-parts/button/src/ghost-button/ghost-button.component.tsx b/ui-parts/button/src/ghost-button/ghost-button.component.tsx deleted file mode 100644 index 94a5c3ccb..000000000 --- a/ui-parts/button/src/ghost-button/ghost-button.component.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { ReactNode } from 'react' - -import type { ButtonProps } from '../button.interfaces.js' - -import { Button } from '../button.component.js' -import { buttonAppearances } from '../styles/appearance.js' -import { buttonShapes } from '../styles/shape.js' - -export const GhostButton = ({ - appearance = buttonAppearances.ghost, - shape = buttonShapes.md, - ...props -}: ButtonProps): ReactNode => + + + ))} + + ))} + + + ) +} + +const meta: Meta = { + title: 'Components/Button', + render: (props) => , + tags: ['autodocs'], + argTypes: { + children: { + description: 'Контент', + table: { + type: { summary: 'ReactNode' }, + }, + }, + focused: { + description: 'Принудительно показать focused-состояние', + control: { type: 'boolean' }, + }, + leadingAddon: { + description: 'Показать leading addon', + control: { type: 'boolean' }, + }, + theme: { + description: 'Тема', + control: { type: 'inline-radio' }, + options: ['light', 'dark'], + }, + trailingAddon: { + description: 'Показать trailing addon', + control: { type: 'boolean' }, + }, + }, +} + +export default meta + +type Story = StoryObj + +export const Variants: Story = { + args: { + children: 'Button', + disabled: false, + focused: false, + fullWidth: false, + leadingAddon: false, + theme: 'light', + trailingAddon: false, + }, +} + +export const FullWidth: Story = { + args: { + ...Variants.args, + fullWidth: true, + leadingAddon: true, + }, +} diff --git a/ui-parts/button/stories/constants.ts b/ui-parts/button/stories/constants.ts new file mode 100644 index 000000000..91c7ade69 --- /dev/null +++ b/ui-parts/button/stories/constants.ts @@ -0,0 +1,16 @@ +export const appearances = ['primary', 'secondary', 'ghost'] as const + +export const appearanceLabels = { + primary: 'Primary', + secondary: 'Secondary', + ghost: 'Ghost', +} as const + +export const shapes = ['xs', 'sm', 'md', 'lg'] as const + +export const shapeLabels = { + xs: 'Xs', + sm: 'Sm', + md: 'Md', + lg: 'Lg', +} as const diff --git a/ui-parts/button/stories/interfaces.ts b/ui-parts/button/stories/interfaces.ts new file mode 100644 index 000000000..62a8c6462 --- /dev/null +++ b/ui-parts/button/stories/interfaces.ts @@ -0,0 +1,11 @@ +export type ThemeName = 'dark' | 'light' + +export interface ButtonStoryProps { + children: string + disabled: boolean + focused: boolean + fullWidth: boolean + leadingAddon: boolean + theme: ThemeName + trailingAddon: boolean +} diff --git a/ui-parts/button/stories/styles.css.ts b/ui-parts/button/stories/styles.css.ts new file mode 100644 index 000000000..84db3db7e --- /dev/null +++ b/ui-parts/button/stories/styles.css.ts @@ -0,0 +1,49 @@ +import { style } from '@vanilla-extract/css' + +import { appearanceVariables } from '../src/styles/appearance/styles.css.js' +import { shapeVariables } from '../src/styles/shape/styles.css.js' + +export const containerStyles = style({ + boxSizing: 'border-box', + display: 'grid', + gap: '32px', + width: '100%', + maxWidth: '720px', + padding: '32px', +}) + +export const appearanceStyles = style({ + display: 'grid', + gap: '12px', +}) + +export const titleStyles = style({ + margin: 0, + color: 'inherit', + fontFamily: 'inherit', + fontSize: '24px', + lineHeight: 1.2, +}) + +export const rowStyles = style({ + display: 'grid', + gridTemplateColumns: '32px minmax(0, 1fr)', + alignItems: 'center', + gap: '16px', +}) + +export const shapeLabelStyles = style({ + color: 'inherit', +}) + +export const buttonContainerStyles = style({ + display: 'flex', + justifyContent: 'flex-end', +}) + +export const focusedStyles = style({ + background: appearanceVariables.focused.background, + color: appearanceVariables.focused.content, + boxShadow: shapeVariables.elevations.focused, + outlineColor: appearanceVariables.focused.border, +}) From 8615e278dfc8f0f6172e6deac378b9300a097239 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Thu, 27 Aug 2026 20:47:34 +0300 Subject: [PATCH 09/20] fix(atls-ui-parts): align button story variants --- ui-parts/card/src/card.stories.tsx | 4 ++-- ui-parts/progress/src/progress.stories.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-parts/card/src/card.stories.tsx b/ui-parts/card/src/card.stories.tsx index 3ab458d36..1d2239566 100644 --- a/ui-parts/card/src/card.stories.tsx +++ b/ui-parts/card/src/card.stories.tsx @@ -65,7 +65,7 @@ export const CardNotify: StoryObj = { name: 'Базовый', render: () => ( Card content}> - @@ -94,7 +94,7 @@ export const CardLarge: StoryObj = { name: 'Много контента', render: () => ( }> - diff --git a/ui-parts/progress/src/progress.stories.tsx b/ui-parts/progress/src/progress.stories.tsx index 917ab6512..22b2bd846 100644 --- a/ui-parts/progress/src/progress.stories.tsx +++ b/ui-parts/progress/src/progress.stories.tsx @@ -90,10 +90,10 @@ export const Dinamic: StoryObj = { - - From 193922f7f427f48b4de2e437c0c8552133cc2086 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Thu, 27 Aug 2026 20:55:35 +0300 Subject: [PATCH 10/20] fix(button): remove obsolete stories --- ui-parts/button/stories/button.stories.tsx | 73 --------------- .../button/stories/ghost-button.stories.tsx | 93 ------------------- 2 files changed, 166 deletions(-) delete mode 100644 ui-parts/button/stories/button.stories.tsx delete mode 100644 ui-parts/button/stories/ghost-button.stories.tsx diff --git a/ui-parts/button/stories/button.stories.tsx b/ui-parts/button/stories/button.stories.tsx deleted file mode 100644 index 91d28f234..000000000 --- a/ui-parts/button/stories/button.stories.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import type { Meta } from '@storybook/react' -import type { StoryObj } from '@storybook/react' - -import { expect } from 'storybook/test' -import { userEvent } from 'storybook/test' -import { within } from 'storybook/test' - -import { Button } from '../src/button.component.js' -import { buttonAppearances } from '../src/styles/appearance.css.js' -import { buttonShapes } from '../src/styles/shape.css.js' - -type ButtonAppearanceName = keyof typeof buttonAppearances -type ButtonShapeName = keyof typeof buttonShapes - -interface ButtonStoryProps { - appearance: ButtonAppearanceName - children: string - shape: ButtonShapeName -} - -const meta: Meta = { - title: 'Components/Button', - render: ({ appearance, shape, ...props }) => ( - - - - ), - tags: ['autodocs'], - argTypes: { - containerWidth: { - name: 'Контейнер', - description: 'Ширина контейнера', - control: { - type: 'range', - min: 200, - max: 600, - step: 25, - }, - }, - fontFamily: { - name: 'Шрифт', - description: 'Шрифт', - control: { type: 'select' }, - options: [vars.fonts.primary], - }, - fontWeight: { - name: 'Насыщенность шрифта', - description: 'Насыщенность шрифта текста', - control: { - type: 'range', - min: 400, - max: 600, - step: 100, - }, - }, - fontSize: { - name: 'Размер шрифта', - description: 'Размер шрифта текста', - control: { type: 'number' }, - }, - fontColor: { - name: 'Цвет текста', - description: 'Цвет текста', - control: { type: 'color' }, - }, - backgroundColor: { - name: 'Цвет заливки', - description: 'Цвет заднего фона', - control: { type: 'color' }, - }, - }, -} - -export default meta - -export const GhostButton: StoryObj = { - args: { - children: 'Кнопка', - containerWidth: 200, - fontFamily: vars.fonts.primary, - fontWeight: 400, - fontColor: 'black', - fontSize: 12, - backgroundColor: 'transparent', - }, -} From 4808b1076e79e56cd4111c0a2db83f06da35f86f Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Thu, 27 Aug 2026 21:15:49 +0300 Subject: [PATCH 11/20] fix(atls-ui-admin): migrate button shape styles --- ui-admin/button/src/styles/shape.css.ts | 30 +++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/ui-admin/button/src/styles/shape.css.ts b/ui-admin/button/src/styles/shape.css.ts index 9300db80e..4d656d50a 100644 --- a/ui-admin/button/src/styles/shape.css.ts +++ b/ui-admin/button/src/styles/shape.css.ts @@ -1,5 +1,31 @@ -import { vars } from '@atls-ui-admin/theme' -import { createShapeStyles } from '@atls-ui-parts/button' +import type { CSSProperties } from 'react' + +import { style } from '@vanilla-extract/css' + +import { vars } from '@atls-ui-admin/theme' + +interface ButtonShapeStyles { + size: number | string + rounding?: CSSProperties['borderRadius'] + paddingLeft?: CSSProperties['paddingLeft'] + paddingRight?: CSSProperties['paddingRight'] +} + +const getDefaultPadding = (size: number | string): number => + typeof size === 'number' ? size * 0.5 : 0 + +const createShapeStyles = ({ + size, + rounding = 0, + paddingLeft, + paddingRight, +}: ButtonShapeStyles): string => + style({ + height: size, + paddingLeft: paddingLeft ?? getDefaultPadding(size), + paddingRight: paddingRight ?? getDefaultPadding(size), + borderRadius: rounding, + }) const hugeStyles = createShapeStyles({ size: 56, From 2b89cbdb4d192c8eaf74c155ce6e94ae8685859e Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Thu, 27 Aug 2026 21:26:18 +0300 Subject: [PATCH 12/20] fix(button): preserve elevation opacity --- ui-parts/button/src/styles/elevation.ts | 69 ------------------------- ui-parts/button/src/styles/variables.ts | 8 --- 2 files changed, 77 deletions(-) delete mode 100644 ui-parts/button/src/styles/elevation.ts diff --git a/ui-parts/button/src/styles/elevation.ts b/ui-parts/button/src/styles/elevation.ts deleted file mode 100644 index d0629d969..000000000 --- a/ui-parts/button/src/styles/elevation.ts +++ /dev/null @@ -1,69 +0,0 @@ -const splitElevation = (elevation: string): Array => { - const shadows: Array = [] - let depth = 0 - let start = 0 - - for (let index = 0; index < elevation.length; index += 1) { - if (elevation[index] === '(') depth += 1 - if (elevation[index] === ')') depth -= 1 - - if (elevation[index] === ',' && depth === 0) { - shadows.push(elevation.slice(start, index).trim()) - start = index + 1 - } - } - - shadows.push(elevation.slice(start).trim()) - - return shadows -} - -const getAlpha = (color: string): number => { - const value = color.trim().toLowerCase() - - if (value === 'transparent') return 0 - - if (value.startsWith('#')) { - const hex = value.slice(1) - - if (hex.length === 4) return Number.parseInt(hex[3].repeat(2), 16) / 255 - if (hex.length === 8) return Number.parseInt(hex.slice(6), 16) / 255 - - return 1 - } - - const openingParenthesis = value.indexOf('(') - const colorFunction = value.slice(0, openingParenthesis) - - if ( - openingParenthesis < 0 || - !value.endsWith(')') || - !['hsl', 'hsla', 'rgb', 'rgba'].includes(colorFunction) - ) { - return 1 - } - - const content = value.slice(openingParenthesis + 1, -1) - const components = content.includes('/') ? content.split('/') : content.split(',') - const alpha = components.length === 2 ? components[1] : components[3] - - if (!alpha) return 1 - - const parsed = Number.parseFloat(alpha) - - return alpha.includes('%') ? parsed / 100 : parsed -} - -export const applyBackgroundAlpha = (elevation: string, background: string): string => { - const alpha = getAlpha(background) - - if (alpha === 1) return elevation - - return splitElevation(elevation) - .map((shadow) => - shadow.replace( - /(#[\da-f]{3,8}|(?:rgb|hsl)a?\([^)]*\)|var\([^)]*\)|transparent|currentcolor)\s*$/i, - (color) => `color-mix(in srgb, ${color} ${alpha * 100}%, transparent)` - )) - .join(', ') -} diff --git a/ui-parts/button/src/styles/variables.ts b/ui-parts/button/src/styles/variables.ts index e096f5ada..b50f4fcfc 100644 --- a/ui-parts/button/src/styles/variables.ts +++ b/ui-parts/button/src/styles/variables.ts @@ -6,7 +6,6 @@ import type { ButtonShapeProperties } from './shape/interfaces.js' import { assignInlineVars } from '@vanilla-extract/dynamic' import { appearanceVariables } from './appearance/styles.css.js' -import { applyBackgroundAlpha } from './elevation.js' import { shapeVariables } from './shape/styles.css.js' export const assignButtonVariables = ( @@ -17,13 +16,6 @@ export const assignButtonVariables = ( ...assignInlineVars(appearanceVariables, appearance), ...assignInlineVars(shapeVariables, { ...shape, - elevations: { - default: applyBackgroundAlpha(shape.elevations.default, appearance.default.background), - hover: applyBackgroundAlpha(shape.elevations.hover, appearance.hover.background), - pressed: applyBackgroundAlpha(shape.elevations.pressed, appearance.pressed.background), - disabled: applyBackgroundAlpha(shape.elevations.disabled, appearance.disabled.background), - focused: applyBackgroundAlpha(shape.elevations.focused, appearance.focused.background), - }, typography: { fontFamily: String(shape.typography.fontFamily ?? 'inherit'), fontSize: String(shape.typography.fontSize ?? 'inherit'), From d89f947cf23ef63d234cbdb2024989b76e0b225f Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Thu, 27 Aug 2026 21:53:12 +0300 Subject: [PATCH 13/20] fix(button): shadow visible content --- ui-parts/button/src/styles/shape/styles.css.ts | 10 +++++----- ui-parts/button/stories/styles.css.ts | 2 +- ui/theme/src/tokens/elevation/interfaces.ts | 3 +-- .../src/tokens/elevation/utils/createElevation.ts | 15 ++++----------- 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/ui-parts/button/src/styles/shape/styles.css.ts b/ui-parts/button/src/styles/shape/styles.css.ts index 75628ae4a..028a9f8ed 100644 --- a/ui-parts/button/src/styles/shape/styles.css.ts +++ b/ui-parts/button/src/styles/shape/styles.css.ts @@ -29,7 +29,7 @@ export const shapeStyles = style({ paddingBlock: shapeVariables.paddingBlock, paddingInline: shapeVariables.paddingInline, borderRadius: shapeVariables.borderRadius, - boxShadow: shapeVariables.elevations.default, + filter: shapeVariables.elevations.default, fontFamily: shapeVariables.typography.fontFamily, fontSize: shapeVariables.typography.fontSize, fontWeight: shapeVariables.typography.fontWeight, @@ -39,16 +39,16 @@ export const shapeStyles = style({ outlineWidth: shapeVariables.borderWidth, selectors: { '&:hover:not(:disabled)': { - boxShadow: shapeVariables.elevations.hover, + filter: shapeVariables.elevations.hover, }, '&:focus-visible:not(:disabled)': { - boxShadow: shapeVariables.elevations.focused, + filter: shapeVariables.elevations.focused, }, '&:active:not(:disabled)': { - boxShadow: shapeVariables.elevations.pressed, + filter: shapeVariables.elevations.pressed, }, '&:disabled': { - boxShadow: shapeVariables.elevations.disabled, + filter: shapeVariables.elevations.disabled, }, }, }) diff --git a/ui-parts/button/stories/styles.css.ts b/ui-parts/button/stories/styles.css.ts index 84db3db7e..c1efe222e 100644 --- a/ui-parts/button/stories/styles.css.ts +++ b/ui-parts/button/stories/styles.css.ts @@ -44,6 +44,6 @@ export const buttonContainerStyles = style({ export const focusedStyles = style({ background: appearanceVariables.focused.background, color: appearanceVariables.focused.content, - boxShadow: shapeVariables.elevations.focused, + filter: shapeVariables.elevations.focused, outlineColor: appearanceVariables.focused.border, }) diff --git a/ui/theme/src/tokens/elevation/interfaces.ts b/ui/theme/src/tokens/elevation/interfaces.ts index e5f5dcbd8..a4e033f92 100644 --- a/ui/theme/src/tokens/elevation/interfaces.ts +++ b/ui/theme/src/tokens/elevation/interfaces.ts @@ -4,7 +4,7 @@ import type { shadowBlurs } from './blurs.js' import type { shadowOffsets } from './offsets.js' import type { shadowSpreads } from './spreads.js' -export type Elevation = NonNullable +export type Elevation = NonNullable export type ShadowBlur = (typeof shadowBlurs)[keyof typeof shadowBlurs] export type ShadowOffset = (typeof shadowOffsets)[keyof typeof shadowOffsets] export type ShadowSpread = (typeof shadowSpreads)[keyof typeof shadowSpreads] @@ -35,4 +35,3 @@ export interface ElevationsTokens { lg: PressableElevationStates modal: ElevationStates } - diff --git a/ui/theme/src/tokens/elevation/utils/createElevation.ts b/ui/theme/src/tokens/elevation/utils/createElevation.ts index 1fc7523d3..d809ff0bf 100644 --- a/ui/theme/src/tokens/elevation/utils/createElevation.ts +++ b/ui/theme/src/tokens/elevation/utils/createElevation.ts @@ -2,19 +2,12 @@ import type { Elevation } from '../interfaces.js' import type { ShadowOptions } from '../interfaces.js' import { shadowOffsets } from '../offsets.js' -import { shadowSpreads } from '../spreads.js' export const createElevation = (...shadows: Array): Elevation => shadows + .filter(({ inset }) => !inset) .map( - ({ - color, - offsetX = shadowOffsets.none, - offsetY, - blur, - spread = shadowSpreads.none, - inset = false, - }) => `${inset ? 'inset ' : ''}${offsetX} ${offsetY} ${blur} ${spread} ${color}` + ({ color, offsetX = shadowOffsets.none, offsetY, blur }) => + `drop-shadow(${offsetX} ${offsetY} ${Number.parseFloat(blur) / 2}px ${color})` ) - .join(', ') - + .join(' ') From a947b88a1bad4595c9e74d89ecec23d0ace705b8 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Thu, 27 Aug 2026 22:37:56 +0300 Subject: [PATCH 14/20] fix(button): restore container elevation rendering --- ui-parts/button/src/styles/shape/styles.css.ts | 10 +++++----- ui-parts/button/stories/styles.css.ts | 2 +- ui/theme/src/tokens/elevation/interfaces.ts | 2 +- .../src/tokens/elevation/utils/createElevation.ts | 14 ++++++++++---- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/ui-parts/button/src/styles/shape/styles.css.ts b/ui-parts/button/src/styles/shape/styles.css.ts index 028a9f8ed..75628ae4a 100644 --- a/ui-parts/button/src/styles/shape/styles.css.ts +++ b/ui-parts/button/src/styles/shape/styles.css.ts @@ -29,7 +29,7 @@ export const shapeStyles = style({ paddingBlock: shapeVariables.paddingBlock, paddingInline: shapeVariables.paddingInline, borderRadius: shapeVariables.borderRadius, - filter: shapeVariables.elevations.default, + boxShadow: shapeVariables.elevations.default, fontFamily: shapeVariables.typography.fontFamily, fontSize: shapeVariables.typography.fontSize, fontWeight: shapeVariables.typography.fontWeight, @@ -39,16 +39,16 @@ export const shapeStyles = style({ outlineWidth: shapeVariables.borderWidth, selectors: { '&:hover:not(:disabled)': { - filter: shapeVariables.elevations.hover, + boxShadow: shapeVariables.elevations.hover, }, '&:focus-visible:not(:disabled)': { - filter: shapeVariables.elevations.focused, + boxShadow: shapeVariables.elevations.focused, }, '&:active:not(:disabled)': { - filter: shapeVariables.elevations.pressed, + boxShadow: shapeVariables.elevations.pressed, }, '&:disabled': { - filter: shapeVariables.elevations.disabled, + boxShadow: shapeVariables.elevations.disabled, }, }, }) diff --git a/ui-parts/button/stories/styles.css.ts b/ui-parts/button/stories/styles.css.ts index c1efe222e..84db3db7e 100644 --- a/ui-parts/button/stories/styles.css.ts +++ b/ui-parts/button/stories/styles.css.ts @@ -44,6 +44,6 @@ export const buttonContainerStyles = style({ export const focusedStyles = style({ background: appearanceVariables.focused.background, color: appearanceVariables.focused.content, - filter: shapeVariables.elevations.focused, + boxShadow: shapeVariables.elevations.focused, outlineColor: appearanceVariables.focused.border, }) diff --git a/ui/theme/src/tokens/elevation/interfaces.ts b/ui/theme/src/tokens/elevation/interfaces.ts index a4e033f92..538a8772d 100644 --- a/ui/theme/src/tokens/elevation/interfaces.ts +++ b/ui/theme/src/tokens/elevation/interfaces.ts @@ -4,7 +4,7 @@ import type { shadowBlurs } from './blurs.js' import type { shadowOffsets } from './offsets.js' import type { shadowSpreads } from './spreads.js' -export type Elevation = NonNullable +export type Elevation = NonNullable export type ShadowBlur = (typeof shadowBlurs)[keyof typeof shadowBlurs] export type ShadowOffset = (typeof shadowOffsets)[keyof typeof shadowOffsets] export type ShadowSpread = (typeof shadowSpreads)[keyof typeof shadowSpreads] diff --git a/ui/theme/src/tokens/elevation/utils/createElevation.ts b/ui/theme/src/tokens/elevation/utils/createElevation.ts index d809ff0bf..ce409fbbd 100644 --- a/ui/theme/src/tokens/elevation/utils/createElevation.ts +++ b/ui/theme/src/tokens/elevation/utils/createElevation.ts @@ -2,12 +2,18 @@ import type { Elevation } from '../interfaces.js' import type { ShadowOptions } from '../interfaces.js' import { shadowOffsets } from '../offsets.js' +import { shadowSpreads } from '../spreads.js' export const createElevation = (...shadows: Array): Elevation => shadows - .filter(({ inset }) => !inset) .map( - ({ color, offsetX = shadowOffsets.none, offsetY, blur }) => - `drop-shadow(${offsetX} ${offsetY} ${Number.parseFloat(blur) / 2}px ${color})` + ({ + color, + offsetX = shadowOffsets.none, + offsetY, + blur, + spread = shadowSpreads.none, + inset = false, + }) => `${inset ? 'inset ' : ''}${offsetX} ${offsetY} ${blur} ${spread} ${color}` ) - .join(' ') + .join(', ') From e3edcc5b1db2196bc6e79e93907d041d25f0ed5f Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Thu, 27 Aug 2026 22:55:03 +0300 Subject: [PATCH 15/20] fix(button): hide elevation without visible paint --- ui-parts/button/src/styles/variables.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ui-parts/button/src/styles/variables.ts b/ui-parts/button/src/styles/variables.ts index b50f4fcfc..99f94608b 100644 --- a/ui-parts/button/src/styles/variables.ts +++ b/ui-parts/button/src/styles/variables.ts @@ -1,6 +1,7 @@ import type { CSSProperties } from 'react' import type { ButtonAppearanceStates } from './appearance/interfaces.js' +import type { ButtonColors } from './appearance/interfaces.js' import type { ButtonShapeProperties } from './shape/interfaces.js' import { assignInlineVars } from '@vanilla-extract/dynamic' @@ -8,6 +9,9 @@ import { assignInlineVars } from '@vanilla-extract/dynamic' import { appearanceVariables } from './appearance/styles.css.js' import { shapeVariables } from './shape/styles.css.js' +const resolveElevation = (elevation: string, colors: ButtonColors): string => + colors.background === 'transparent' && colors.border === 'transparent' ? 'none' : elevation + export const assignButtonVariables = ( appearance: ButtonAppearanceStates, shape: ButtonShapeProperties, @@ -16,6 +20,13 @@ export const assignButtonVariables = ( ...assignInlineVars(appearanceVariables, appearance), ...assignInlineVars(shapeVariables, { ...shape, + elevations: { + default: resolveElevation(shape.elevations.default, appearance.default), + hover: resolveElevation(shape.elevations.hover, appearance.hover), + pressed: resolveElevation(shape.elevations.pressed, appearance.pressed), + disabled: resolveElevation(shape.elevations.disabled, appearance.disabled), + focused: resolveElevation(shape.elevations.focused, appearance.focused), + }, typography: { fontFamily: String(shape.typography.fontFamily ?? 'inherit'), fontSize: String(shape.typography.fontSize ?? 'inherit'), From b3897ad5947ee74df4d069e735b0f64bed76758c Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Fri, 28 Aug 2026 00:19:31 +0300 Subject: [PATCH 16/20] fix(button): preserve class shape styles --- ui-parts/button/src/component.tsx | 2 +- ui-parts/button/src/styles/shape/variants.ts | 7 ++-- ui-parts/button/src/styles/variables.ts | 38 ++++++++++---------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/ui-parts/button/src/component.tsx b/ui-parts/button/src/component.tsx index fa341ed66..e12227547 100644 --- a/ui-parts/button/src/component.tsx +++ b/ui-parts/button/src/component.tsx @@ -45,7 +45,7 @@ export const Button = ({ className={clsx( baseStyles, appearanceStyles, - shapeStyles, + resolvedShape && shapeStyles, shapeClassName, fullWidth && fullWidthStyles, className diff --git a/ui-parts/button/src/styles/shape/variants.ts b/ui-parts/button/src/styles/shape/variants.ts index 4d17128f8..8af9e69c7 100644 --- a/ui-parts/button/src/styles/shape/variants.ts +++ b/ui-parts/button/src/styles/shape/variants.ts @@ -80,10 +80,13 @@ export const buttonShapes = { export const isButtonShapeName = (shape: string): shape is ButtonShapeName => shape in buttonShapes -export const resolveButtonShape = (shape: ButtonShape, theme: Theme): ButtonShapeProperties => { +export const resolveButtonShape = ( + shape: ButtonShape, + theme: Theme +): ButtonShapeProperties | undefined => { if (typeof shape !== 'string') { return shape } - return variants(theme)[isButtonShapeName(shape) ? shape : buttonShapes.md] + return isButtonShapeName(shape) ? variants(theme)[shape] : undefined } diff --git a/ui-parts/button/src/styles/variables.ts b/ui-parts/button/src/styles/variables.ts index 99f94608b..d8dbdf02b 100644 --- a/ui-parts/button/src/styles/variables.ts +++ b/ui-parts/button/src/styles/variables.ts @@ -14,26 +14,28 @@ const resolveElevation = (elevation: string, colors: ButtonColors): string => export const assignButtonVariables = ( appearance: ButtonAppearanceStates, - shape: ButtonShapeProperties, + shape?: ButtonShapeProperties, style?: CSSProperties ): CSSProperties => ({ ...assignInlineVars(appearanceVariables, appearance), - ...assignInlineVars(shapeVariables, { - ...shape, - elevations: { - default: resolveElevation(shape.elevations.default, appearance.default), - hover: resolveElevation(shape.elevations.hover, appearance.hover), - pressed: resolveElevation(shape.elevations.pressed, appearance.pressed), - disabled: resolveElevation(shape.elevations.disabled, appearance.disabled), - focused: resolveElevation(shape.elevations.focused, appearance.focused), - }, - typography: { - fontFamily: String(shape.typography.fontFamily ?? 'inherit'), - fontSize: String(shape.typography.fontSize ?? 'inherit'), - fontWeight: String(shape.typography.fontWeight ?? 'inherit'), - letterSpacing: String(shape.typography.letterSpacing ?? 'inherit'), - lineHeight: String(shape.typography.lineHeight ?? 'inherit'), - }, - }), + ...(shape + ? assignInlineVars(shapeVariables, { + ...shape, + elevations: { + default: resolveElevation(shape.elevations.default, appearance.default), + hover: resolveElevation(shape.elevations.hover, appearance.hover), + pressed: resolveElevation(shape.elevations.pressed, appearance.pressed), + disabled: resolveElevation(shape.elevations.disabled, appearance.disabled), + focused: resolveElevation(shape.elevations.focused, appearance.focused), + }, + typography: { + fontFamily: String(shape.typography.fontFamily ?? 'inherit'), + fontSize: String(shape.typography.fontSize ?? 'inherit'), + fontWeight: String(shape.typography.fontWeight ?? 'inherit'), + letterSpacing: String(shape.typography.letterSpacing ?? 'inherit'), + lineHeight: String(shape.typography.lineHeight ?? 'inherit'), + }, + }) + : {}), ...style, }) From d14d544468cb33c1855d1d53206615f9f5244ab8 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Fri, 28 Aug 2026 00:52:34 +0300 Subject: [PATCH 17/20] fix(button): preserve addon layout for class shapes --- ui-parts/button/src/component.tsx | 2 +- ui-parts/button/src/styles/shape/variants.ts | 7 ++-- ui-parts/button/src/styles/variables.ts | 38 ++++++++++---------- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/ui-parts/button/src/component.tsx b/ui-parts/button/src/component.tsx index e12227547..9cf1351b3 100644 --- a/ui-parts/button/src/component.tsx +++ b/ui-parts/button/src/component.tsx @@ -45,7 +45,7 @@ export const Button = ({ className={clsx( baseStyles, appearanceStyles, - resolvedShape && shapeStyles, + !shapeClassName && shapeStyles, shapeClassName, fullWidth && fullWidthStyles, className diff --git a/ui-parts/button/src/styles/shape/variants.ts b/ui-parts/button/src/styles/shape/variants.ts index 8af9e69c7..4d17128f8 100644 --- a/ui-parts/button/src/styles/shape/variants.ts +++ b/ui-parts/button/src/styles/shape/variants.ts @@ -80,13 +80,10 @@ export const buttonShapes = { export const isButtonShapeName = (shape: string): shape is ButtonShapeName => shape in buttonShapes -export const resolveButtonShape = ( - shape: ButtonShape, - theme: Theme -): ButtonShapeProperties | undefined => { +export const resolveButtonShape = (shape: ButtonShape, theme: Theme): ButtonShapeProperties => { if (typeof shape !== 'string') { return shape } - return isButtonShapeName(shape) ? variants(theme)[shape] : undefined + return variants(theme)[isButtonShapeName(shape) ? shape : buttonShapes.md] } diff --git a/ui-parts/button/src/styles/variables.ts b/ui-parts/button/src/styles/variables.ts index d8dbdf02b..99f94608b 100644 --- a/ui-parts/button/src/styles/variables.ts +++ b/ui-parts/button/src/styles/variables.ts @@ -14,28 +14,26 @@ const resolveElevation = (elevation: string, colors: ButtonColors): string => export const assignButtonVariables = ( appearance: ButtonAppearanceStates, - shape?: ButtonShapeProperties, + shape: ButtonShapeProperties, style?: CSSProperties ): CSSProperties => ({ ...assignInlineVars(appearanceVariables, appearance), - ...(shape - ? assignInlineVars(shapeVariables, { - ...shape, - elevations: { - default: resolveElevation(shape.elevations.default, appearance.default), - hover: resolveElevation(shape.elevations.hover, appearance.hover), - pressed: resolveElevation(shape.elevations.pressed, appearance.pressed), - disabled: resolveElevation(shape.elevations.disabled, appearance.disabled), - focused: resolveElevation(shape.elevations.focused, appearance.focused), - }, - typography: { - fontFamily: String(shape.typography.fontFamily ?? 'inherit'), - fontSize: String(shape.typography.fontSize ?? 'inherit'), - fontWeight: String(shape.typography.fontWeight ?? 'inherit'), - letterSpacing: String(shape.typography.letterSpacing ?? 'inherit'), - lineHeight: String(shape.typography.lineHeight ?? 'inherit'), - }, - }) - : {}), + ...assignInlineVars(shapeVariables, { + ...shape, + elevations: { + default: resolveElevation(shape.elevations.default, appearance.default), + hover: resolveElevation(shape.elevations.hover, appearance.hover), + pressed: resolveElevation(shape.elevations.pressed, appearance.pressed), + disabled: resolveElevation(shape.elevations.disabled, appearance.disabled), + focused: resolveElevation(shape.elevations.focused, appearance.focused), + }, + typography: { + fontFamily: String(shape.typography.fontFamily ?? 'inherit'), + fontSize: String(shape.typography.fontSize ?? 'inherit'), + fontWeight: String(shape.typography.fontWeight ?? 'inherit'), + letterSpacing: String(shape.typography.letterSpacing ?? 'inherit'), + lineHeight: String(shape.typography.lineHeight ?? 'inherit'), + }, + }), ...style, }) From 6862f693e3d5282b0b23e0077c2ccfbdd7cea846 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Fri, 28 Aug 2026 21:49:28 +0300 Subject: [PATCH 18/20] refactor(button): simplify shape styles --- ui-parts/button/src/component.tsx | 10 +-- ui-parts/button/src/index.ts | 1 - ui-parts/button/src/interfaces.ts | 3 +- .../button/src/styles/layout/addons.css.ts | 14 +-- .../button/src/styles/layout/content.css.ts | 11 ++- ui-parts/button/src/styles/reset.css.ts | 5 ++ ui-parts/button/src/styles/shape/index.ts | 4 +- .../button/src/styles/shape/interfaces.ts | 27 ------ .../button/src/styles/shape/styles.css.ts | 54 ----------- .../button/src/styles/shape/variants.css.ts | 84 +++++++++++++++++ ui-parts/button/src/styles/shape/variants.ts | 89 ------------------- ui-parts/button/src/styles/variables.ts | 56 +++++++----- ui-parts/button/stories/component.stories.tsx | 2 +- ui-parts/button/stories/styles.css.ts | 4 +- 14 files changed, 145 insertions(+), 219 deletions(-) delete mode 100644 ui-parts/button/src/styles/shape/interfaces.ts delete mode 100644 ui-parts/button/src/styles/shape/styles.css.ts create mode 100644 ui-parts/button/src/styles/shape/variants.css.ts delete mode 100644 ui-parts/button/src/styles/shape/variants.ts diff --git a/ui-parts/button/src/component.tsx b/ui-parts/button/src/component.tsx index 9cf1351b3..d10e8f141 100644 --- a/ui-parts/button/src/component.tsx +++ b/ui-parts/button/src/component.tsx @@ -15,11 +15,8 @@ import { buttonShapes } from './styles/index.js' import { contentStyles } from './styles/index.js' import { fullWidthContentStyles } from './styles/index.js' import { fullWidthStyles } from './styles/index.js' -import { isButtonShapeName } from './styles/index.js' import { labelStyles } from './styles/index.js' import { resolveButtonAppearance } from './styles/index.js' -import { resolveButtonShape } from './styles/index.js' -import { shapeStyles } from './styles/index.js' export const Button = ({ appearance = buttonAppearances.primary, @@ -34,19 +31,16 @@ export const Button = ({ }: ButtonProps): ReactNode => { const theme = useTheme() const resolvedAppearance = resolveButtonAppearance(appearance, theme) - const resolvedShape = resolveButtonShape(shape, theme) - const shapeClassName = typeof shape === 'string' && !isButtonShapeName(shape) ? shape : undefined return (