fulldev-ui 0.5.0-beta.9 → 0.6.0-beta-4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/.env.example +3 -0
  2. package/.prettierrc.yaml +1 -0
  3. package/astro.config.ts +10 -34
  4. package/cloudcannon.config.yml +217 -0
  5. package/package.json +25 -29
  6. package/pnpm-lock.yaml +943 -2157
  7. package/public/favicon/android-chrome-192x192.png +0 -0
  8. package/public/favicon/android-chrome-512x512.png +0 -0
  9. package/public/favicon/apple-touch-icon.png +0 -0
  10. package/public/favicon/manifest.webmanifest +7 -3
  11. package/public/favicon/mstile-150x150.png +0 -0
  12. package/src/blocks/Banner.astro +14 -29
  13. package/src/blocks/Contact1.astro +92 -0
  14. package/src/blocks/Cta1.astro +72 -0
  15. package/src/blocks/Feature1.astro +67 -0
  16. package/src/blocks/Features1.astro +75 -0
  17. package/src/blocks/Footer.astro +100 -3
  18. package/src/blocks/Header.astro +192 -3
  19. package/src/blocks/Hero1.astro +74 -0
  20. package/src/blocks/Job1.astro +97 -0
  21. package/src/blocks/Products1.astro +90 -0
  22. package/src/blocks/Reviews1.astro +75 -0
  23. package/src/blocks/_Hero.astro +245 -0
  24. package/src/components/Avatar.astro +28 -9
  25. package/src/components/Avatars.astro +36 -0
  26. package/src/components/Badge.astro +63 -24
  27. package/src/components/Badges.astro +16 -23
  28. package/src/components/Banner.astro +26 -0
  29. package/src/components/Block.astro +49 -0
  30. package/src/components/Blocks.astro +26 -0
  31. package/src/components/Breadcrumb.astro +58 -0
  32. package/src/components/Button.astro +49 -119
  33. package/src/components/Buttons.astro +23 -13
  34. package/src/components/Card.astro +9 -137
  35. package/src/components/Cards.astro +33 -35
  36. package/src/components/Carousel.astro +106 -0
  37. package/src/components/Cart.astro +18 -14
  38. package/src/components/CartAdd.svelte +76 -0
  39. package/src/components/CartCheckout.svelte +58 -0
  40. package/src/components/CartForm.svelte +130 -0
  41. package/src/components/CartList.svelte +88 -0
  42. package/src/components/CartQuantity.svelte +81 -0
  43. package/src/components/CartSummary.astro +16 -105
  44. package/src/components/CartTotal.svelte +15 -0
  45. package/src/components/Channel.astro +44 -35
  46. package/src/components/Channels.astro +20 -6
  47. package/src/components/Checkbox.astro +52 -27
  48. package/src/components/Code.astro +52 -0
  49. package/src/components/Container.astro +25 -0
  50. package/src/components/Dialog.astro +3 -2
  51. package/src/components/Drawer.astro +55 -146
  52. package/src/components/DrawerOld.astro +143 -0
  53. package/src/components/Element.astro +18 -8
  54. package/src/components/Form.astro +16 -13
  55. package/src/components/Gallery.astro +174 -0
  56. package/src/components/Head.astro +62 -25
  57. package/src/components/Header.astro +10 -115
  58. package/src/components/Heading.astro +51 -44
  59. package/src/components/Icon.astro +29 -33
  60. package/src/components/Image.astro +18 -68
  61. package/src/components/Input.astro +65 -51
  62. package/src/components/Label.astro +20 -15
  63. package/src/components/Layout.astro +35 -0
  64. package/src/components/Link.astro +51 -32
  65. package/src/components/Links.astro +42 -0
  66. package/src/components/List.astro +41 -16
  67. package/src/components/LocaleSwitcher.astro +2 -2
  68. package/src/components/Logo.astro +56 -0
  69. package/src/components/Menu.astro +21 -17
  70. package/src/components/Navigation.astro +39 -0
  71. package/src/components/Panel.astro +25 -0
  72. package/src/components/Paragraph.astro +55 -0
  73. package/src/components/Price.astro +4 -4
  74. package/src/components/Prose.astro +78 -0
  75. package/src/components/Rating.astro +23 -62
  76. package/src/components/Search.astro +107 -43
  77. package/src/components/Section.astro +13 -242
  78. package/src/components/Select.astro +45 -55
  79. package/src/components/Sidebar.astro +53 -76
  80. package/src/components/Social.astro +11 -9
  81. package/src/components/Socials.astro +9 -5
  82. package/src/components/Subheader.astro +59 -0
  83. package/src/components/Switch.astro +72 -29
  84. package/src/components/Table.astro +1 -1
  85. package/src/components/Tagline.astro +32 -14
  86. package/src/components/Textarea.astro +40 -50
  87. package/src/components/Themer.astro +76 -0
  88. package/src/components/Toc.astro +32 -31
  89. package/src/components/_Author.astro +90 -0
  90. package/src/components/_Header.astro +129 -0
  91. package/src/components/_Image.astro +79 -0
  92. package/src/components/_Prose copy.astro +172 -0
  93. package/src/components/_Sidebar copy.astro +87 -0
  94. package/src/components/docs/PropsTable.astro +38 -264
  95. package/src/components/experimental/Window.astro +5 -57
  96. package/src/content/config.ts +4 -10
  97. package/src/content/{presets → layouts}/base.yml +52 -73
  98. package/src/content/pages/blocks/_categories.mdx +0 -2
  99. package/src/content/pages/blocks/_footer.mdx +0 -2
  100. package/src/content/pages/blocks/_header.mdx +0 -4
  101. package/src/content/pages/blocks/_jobs.mdx +0 -2
  102. package/src/content/pages/blocks/_prices.mdx +0 -2
  103. package/src/content/pages/blocks/_product.mdx +0 -2
  104. package/src/content/pages/blocks/_products.mdx +0 -2
  105. package/src/content/pages/blocks/_stats.mdx +0 -2
  106. package/src/content/pages/blocks/banner.mdx +0 -2
  107. package/src/content/pages/blocks/colleagues.mdx +0 -3
  108. package/src/content/pages/blocks/contact.mdx +0 -2
  109. package/src/content/pages/blocks/content.mdx +0 -6
  110. package/src/content/pages/blocks/cta.mdx +0 -10
  111. package/src/content/pages/blocks/features.mdx +0 -5
  112. package/src/content/pages/blocks/hero.mdx +0 -5
  113. package/src/content/pages/blocks/intro.mdx +0 -5
  114. package/src/content/pages/blocks/posts.mdx +0 -2
  115. package/src/content/pages/blocks/reviews.mdx +0 -3
  116. package/src/content/pages/categories/category.md +7 -0
  117. package/src/content/pages/docs/_internationalization.mdx +55 -0
  118. package/src/content/pages/docs/_using-blocks.mdx +33 -0
  119. package/src/content/pages/docs/_using-presets.mdx +49 -0
  120. package/src/content/pages/docs/blocks.mdx +33 -0
  121. package/src/content/pages/docs/collections/images.mdx +102 -0
  122. package/src/content/pages/docs/collections/pages.mdx +119 -0
  123. package/src/content/pages/docs/collections/presets.mdx +60 -0
  124. package/src/content/pages/docs/collections.mdx +42 -0
  125. package/src/content/pages/{base/card.mdx → docs/components/_card.mdx} +2 -5
  126. package/src/content/pages/{base → docs/components}/_dialog.mdx +1 -2
  127. package/src/content/pages/{base → docs/components}/_drawer.mdx +1 -2
  128. package/src/content/pages/{base → docs/components}/_localeswitcher.mdx +1 -2
  129. package/src/content/pages/{base → docs/components}/_switch.mdx +3 -4
  130. package/src/content/pages/{typography → docs/components}/_tagline.mdx +3 -4
  131. package/src/content/pages/docs/components/_toggle.mdx +30 -0
  132. package/src/content/pages/docs/components/avatar.mdx +34 -0
  133. package/src/content/pages/{base → docs/components}/badge.mdx +28 -10
  134. package/src/content/pages/docs/components/badges.mdx +46 -0
  135. package/src/content/pages/docs/components/button.mdx +78 -0
  136. package/src/content/pages/docs/components/buttons.mdx +51 -0
  137. package/src/content/pages/docs/components/channel.mdx +91 -0
  138. package/src/content/pages/docs/components/channels.mdx +89 -0
  139. package/src/content/pages/docs/components/checkbox.mdx +34 -0
  140. package/src/content/pages/docs/components/code.mdx +45 -0
  141. package/src/content/pages/docs/components/heading.mdx +59 -0
  142. package/src/content/pages/docs/components/icon.mdx +48 -0
  143. package/src/content/pages/docs/components/image.mdx +25 -0
  144. package/src/content/pages/docs/components/input.mdx +40 -0
  145. package/src/content/pages/{typography → docs/components}/label.mdx +9 -6
  146. package/src/content/pages/{typography → docs/components}/link.mdx +24 -10
  147. package/src/content/pages/docs/components/links.mdx +99 -0
  148. package/src/content/pages/docs/components/list.mdx +44 -0
  149. package/src/content/pages/docs/components/logo.mdx +36 -0
  150. package/src/content/pages/docs/components/menu.mdx +89 -0
  151. package/src/content/pages/docs/components/prose.mdx +36 -0
  152. package/src/content/pages/docs/components/rating.mdx +76 -0
  153. package/src/content/pages/{base → docs/components}/section.mdx +2 -3
  154. package/src/content/pages/{base → docs/components}/select.mdx +1 -11
  155. package/src/content/pages/docs/components/social.mdx +94 -0
  156. package/src/content/pages/docs/components/socials.mdx +102 -0
  157. package/src/content/pages/docs/components/switch.mdx +34 -0
  158. package/src/content/pages/{base → docs/components}/table.mdx +1 -4
  159. package/src/content/pages/docs/components/tagline.mdx +46 -0
  160. package/src/content/pages/docs/components/text.mdx +48 -0
  161. package/src/content/pages/docs/components/textarea.mdx +34 -0
  162. package/src/content/pages/docs/components/themer.mdx +49 -0
  163. package/src/content/pages/docs/components.mdx +37 -0
  164. package/src/content/pages/docs/customization.mdx +151 -0
  165. package/src/content/pages/docs/frameworks.mdx +29 -0
  166. package/src/content/pages/docs/generating-pages.mdx +129 -0
  167. package/src/content/pages/docs/index.mdx +20 -0
  168. package/src/content/pages/docs/installation.mdx +50 -0
  169. package/src/content/pages/docs/layouts.mdx +33 -0
  170. package/src/content/pages/index.mdx +25 -33
  171. package/src/content/pages/posts/post.md +7 -0
  172. package/src/content/pages/products/product.md +20 -0
  173. package/src/content/pages/showcase/anti-slip-profiles.md +2 -2
  174. package/src/content/pages/showcase/fulldev-ui.md +1 -1
  175. package/src/content/pages/showcase/lavis-detailing.md +1 -1
  176. package/src/content/pages/showcase/leren-trainingsacteren.md +1 -1
  177. package/src/content/pages/showcase/miele-witgoedcenter-amsterdam.md +1 -1
  178. package/src/content/pages/showcase/sloepverhuur-bolsward.md +1 -1
  179. package/src/content/pages/showcase/stark-vulcanising-products.md +1 -1
  180. package/src/content/pages/showcase/the-gym-haren.md +1 -1
  181. package/src/content/pages/showcase/van-dillen-antieke-bouwmaterialen.md +1 -1
  182. package/src/content/pages/showcase.mdx +4 -3
  183. package/src/css/color.css +0 -10
  184. package/src/css/custom.css +10 -12
  185. package/src/css/size.css +2 -11
  186. package/src/css/theme.css +95 -0
  187. package/src/env.d.ts +1 -0
  188. package/src/images/favicon/android-chrome-192x192.png +0 -0
  189. package/src/images/favicon/android-chrome-512x512.png +0 -0
  190. package/src/images/favicon/apple-touch-icon.png +0 -0
  191. package/src/images/favicon/browserconfig.xml +9 -0
  192. package/src/images/favicon/favicon-16x16.png +0 -0
  193. package/src/images/favicon/favicon-32x32.png +0 -0
  194. package/src/images/favicon/favicon-48x48.png +0 -0
  195. package/src/images/favicon/favicon.ico +0 -0
  196. package/src/images/favicon/favicon.svg +16 -0
  197. package/src/images/favicon/manifest.webmanifest +30 -0
  198. package/src/images/favicon/mstile-150x150.png +0 -0
  199. package/src/images/favicon/safari-pinned-tab.svg +16 -0
  200. package/src/images/favicon/yandex-browser-50x50.png +0 -0
  201. package/src/images/favicon/yandex-browser-manifest.json +9 -0
  202. package/src/images/logo-1.svg +11 -0
  203. package/src/images/logo-2.svg +13 -0
  204. package/src/images/logo-basic.svg +10 -0
  205. package/src/images/logo-simple.svg +13 -0
  206. package/src/images/logo-thin.svg +15 -0
  207. package/src/images/logo.svg +14 -0
  208. package/src/images/logofat.svg +11 -0
  209. package/src/integration/index.ts +110 -140
  210. package/src/layouts/Category.astro +131 -0
  211. package/src/layouts/Doc.astro +72 -0
  212. package/src/layouts/Job.astro +64 -0
  213. package/src/layouts/Page.astro +30 -0
  214. package/src/layouts/Post.astro +20 -0
  215. package/src/layouts/Product.astro +110 -0
  216. package/src/pages/[...page].astro +29 -24
  217. package/src/pages/test.astro +37 -0
  218. package/src/schemas/blocks/banner.ts +10 -0
  219. package/src/schemas/blocks/categories.ts +14 -0
  220. package/src/schemas/blocks/contact.ts +18 -0
  221. package/src/schemas/blocks/cta.ts +19 -0
  222. package/src/schemas/blocks/feature.ts +18 -0
  223. package/src/schemas/blocks/features.ts +24 -0
  224. package/src/schemas/blocks/hero.ts +20 -0
  225. package/src/schemas/blocks/intro.ts +16 -0
  226. package/src/schemas/blocks/products.ts +14 -0
  227. package/src/schemas/blocks/reviews.ts +24 -0
  228. package/src/schemas/components/avatar.ts +4 -1
  229. package/src/schemas/components/badge.ts +16 -11
  230. package/src/schemas/components/block.ts +22 -0
  231. package/src/schemas/components/button.ts +17 -14
  232. package/src/schemas/components/channel.ts +15 -9
  233. package/src/schemas/components/footer.ts +15 -21
  234. package/src/schemas/components/head.ts +17 -0
  235. package/src/schemas/components/header.ts +19 -7
  236. package/src/schemas/components/heading.ts +7 -1
  237. package/src/schemas/components/icon.ts +7 -1
  238. package/src/schemas/components/image.ts +38 -16
  239. package/src/schemas/components/link.ts +17 -0
  240. package/src/schemas/components/logo.ts +12 -2
  241. package/src/schemas/components/menu.ts +11 -11
  242. package/src/schemas/components/navigation.ts +10 -0
  243. package/src/schemas/components/paragraph.ts +9 -0
  244. package/src/schemas/components/price.ts +7 -1
  245. package/src/schemas/components/rating.ts +8 -13
  246. package/src/schemas/components/sidebar.ts +14 -11
  247. package/src/schemas/components/social.ts +16 -1
  248. package/src/schemas/components/tagline.ts +7 -1
  249. package/src/schemas/imageSchema.ts +30 -1
  250. package/src/schemas/layoutSchema.ts +22 -0
  251. package/src/schemas/pageSchema.ts +26 -96
  252. package/src/stores/cart.svelte.ts +79 -0
  253. package/src/stores/cart.ts +59 -6
  254. package/src/types/global-props.ts +0 -9
  255. package/src/types/{virtual-modules.d.ts → virtual.d.ts} +1 -1
  256. package/src/utils/cart.ts +1 -10
  257. package/src/utils/formatEntries.ts +8 -0
  258. package/src/utils/formatEntry.ts +29 -0
  259. package/src/utils/getDeepReferenceChildren.ts +14 -0
  260. package/src/utils/getHref.ts +6 -0
  261. package/src/utils/getReferenceChildren.ts +1 -1
  262. package/src/utils/setTheme.ts +18 -0
  263. package/src/utils/slugify.ts +6 -0
  264. package/svelte.config.js +5 -0
  265. package/tailwind.config.ts +67 -0
  266. package/tsconfig.json +1 -3
  267. package/src/blocks/Article.astro +0 -40
  268. package/src/blocks/Categories.astro +0 -12
  269. package/src/blocks/Colleagues.astro +0 -13
  270. package/src/blocks/Contact.astro +0 -116
  271. package/src/blocks/Content.astro +0 -12
  272. package/src/blocks/Cta.astro +0 -15
  273. package/src/blocks/Excerpt.astro +0 -14
  274. package/src/blocks/Features.astro +0 -11
  275. package/src/blocks/Hero.astro +0 -15
  276. package/src/blocks/Intro.astro +0 -23
  277. package/src/blocks/Jobs.astro +0 -12
  278. package/src/blocks/Posts.astro +0 -11
  279. package/src/blocks/Prices.astro +0 -24
  280. package/src/blocks/Product.astro +0 -110
  281. package/src/blocks/Products.astro +0 -11
  282. package/src/blocks/Reviews.astro +0 -13
  283. package/src/components/CartForm.astro +0 -121
  284. package/src/components/CartList.astro +0 -134
  285. package/src/components/Footer.astro +0 -193
  286. package/src/components/Text.astro +0 -43
  287. package/src/components/ThemeSelect.astro +0 -41
  288. package/src/components/Writeup.astro +0 -90
  289. package/src/components/experimental/Form.astro +0 -53
  290. package/src/components/experimental/Gallery.astro +0 -104
  291. package/src/components/experimental/Logo.astro +0 -38
  292. package/src/components/experimental/Prose.astro +0 -192
  293. package/src/components/sections.astro +0 -28
  294. package/src/content/pages/base/button.mdx +0 -60
  295. package/src/content/pages/base/checkbox.mdx +0 -42
  296. package/src/content/pages/base/icon.mdx +0 -55
  297. package/src/content/pages/base/image.mdx +0 -42
  298. package/src/content/pages/base/input.mdx +0 -56
  299. package/src/content/pages/base/rating.mdx +0 -55
  300. package/src/content/pages/base/textarea.mdx +0 -58
  301. package/src/content/pages/base/theme-select.mdx +0 -15
  302. package/src/content/pages/layouts/_layout.mdx +0 -14
  303. package/src/content/pages/layouts/docslayout.mdx +0 -14
  304. package/src/content/pages/overview/components.mdx +0 -75
  305. package/src/content/pages/overview/content-collections.mdx +0 -139
  306. package/src/content/pages/overview/frameworks.mdx +0 -198
  307. package/src/content/pages/overview/installation.mdx +0 -53
  308. package/src/content/pages/overview/introduction.mdx +0 -30
  309. package/src/content/pages/overview/theming.mdx +0 -69
  310. package/src/content/pages/schemas/page.mdx +0 -17
  311. package/src/content/pages/schemas/preset.mdx +0 -9
  312. package/src/content/pages/showcase/record.md +0 -3
  313. package/src/content/pages/typography/heading.mdx +0 -73
  314. package/src/content/pages/typography/list.mdx +0 -51
  315. package/src/content/pages/typography/text.mdx +0 -61
  316. package/src/content/presets/blocks.yml +0 -1
  317. package/src/content/presets/docs.yml +0 -6
  318. package/src/css/space.css +0 -77
  319. package/src/css/styles.css +0 -0
  320. package/src/images/avatar-1.yml +0 -1
  321. package/src/integration/generate-colors.ts +0 -639
  322. package/src/integration/tailwind-plugin.ts +0 -80
  323. package/src/integration/unocss-preset.ts +0 -84
  324. package/src/layouts/BlockLayout.astro +0 -19
  325. package/src/layouts/ContactLayout.astro +0 -21
  326. package/src/layouts/DocsLayout.astro +0 -73
  327. package/src/layouts/Layout.astro +0 -163
  328. package/src/layouts/OverviewLayout.astro +0 -42
  329. package/src/layouts/PostLayout.astro +0 -21
  330. package/src/layouts/ProductLayout.astro +0 -21
  331. package/src/loaders/imageLoader.ts +0 -26
  332. package/src/schemas/components/avatars.ts +0 -4
  333. package/src/schemas/components/buttons.ts +0 -4
  334. package/src/schemas/components/card.ts +0 -25
  335. package/src/schemas/components/cards.ts +0 -4
  336. package/src/schemas/components/channels.ts +0 -4
  337. package/src/schemas/components/images.ts +0 -4
  338. package/src/schemas/components/list.ts +0 -3
  339. package/src/schemas/components/section.ts +0 -15
  340. package/src/schemas/components/sections.ts +0 -3
  341. package/src/schemas/components/socials.ts +0 -4
  342. package/src/schemas/components/text.ts +0 -3
  343. package/src/schemas/components/toc.ts +0 -3
  344. package/src/schemas/components/writeup.ts +0 -20
  345. package/src/schemas/presetSchema.ts +0 -3
  346. package/src/schemas/utils/navigation.ts +0 -8
  347. package/src/schemas/utils/parents.ts +0 -3
@@ -1,639 +0,0 @@
1
- // Below code is taken from Radix UI, therefore it does not satisfy Astro strictest tsconfig.
2
- // @ts-nocheck
3
- import * as RadixColors from '@radix-ui/colors'
4
- import BezierEasing from 'bezier-easing'
5
- import Color from 'colorjs.io'
6
-
7
- type ArrayOf12<T> = [T, T, T, T, T, T, T, T, T, T, T, T]
8
- const arrayOf12 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] as const
9
-
10
- // prettier-ignore
11
- const grayScaleNames = ['gray', 'mauve', 'slate', 'sage', 'olive', 'sand'] as const;
12
-
13
- // prettier-ignore
14
- const scaleNames = [...grayScaleNames, 'tomato', 'red', 'ruby', 'crimson', 'pink',
15
- 'plum', 'purple', 'violet', 'iris', 'indigo', 'blue', 'cyan', 'teal', 'jade', 'green',
16
- 'grass', 'brown', 'orange', 'sky', 'mint', 'lime', 'yellow', 'amber'] as const;
17
-
18
- const lightColors = Object.fromEntries(
19
- scaleNames.map((scaleName) => [
20
- scaleName,
21
- Object.values(RadixColors[`${scaleName}P3`]).map((str) =>
22
- new Color(str).to('oklch')
23
- ),
24
- ])
25
- ) as Record<(typeof scaleNames)[number], ArrayOf12<Color>>
26
-
27
- const darkColors = Object.fromEntries(
28
- scaleNames.map((scaleName) => [
29
- scaleName,
30
- Object.values(RadixColors[`${scaleName}DarkP3`]).map((str) =>
31
- new Color(str).to('oklch')
32
- ),
33
- ])
34
- ) as Record<(typeof scaleNames)[number], ArrayOf12<Color>>
35
-
36
- const lightGrayColors = Object.fromEntries(
37
- grayScaleNames.map((scaleName) => [
38
- scaleName,
39
- Object.values(RadixColors[`${scaleName}P3`]).map((str) =>
40
- new Color(str).to('oklch')
41
- ),
42
- ])
43
- ) as Record<(typeof grayScaleNames)[number], ArrayOf12<Color>>
44
-
45
- const darkGrayColors = Object.fromEntries(
46
- grayScaleNames.map((scaleName) => [
47
- scaleName,
48
- Object.values(RadixColors[`${scaleName}DarkP3`]).map((str) =>
49
- new Color(str).to('oklch')
50
- ),
51
- ])
52
- ) as Record<(typeof grayScaleNames)[number], ArrayOf12<Color>>
53
-
54
- export const generateRadixColors = ({
55
- appearance,
56
- ...args
57
- }: {
58
- appearance: 'light' | 'dark'
59
- accent: string
60
- gray: string
61
- background: string
62
- }) => {
63
- const allScales = appearance === 'light' ? lightColors : darkColors
64
- const grayScales = appearance === 'light' ? lightGrayColors : darkGrayColors
65
- const backgroundColor = new Color(args.background).to('oklch')
66
-
67
- const grayBaseColor = new Color(args.gray).to('oklch')
68
- const grayScaleColors = getScaleFromColor(
69
- grayBaseColor,
70
- grayScales,
71
- backgroundColor
72
- )
73
-
74
- const accentBaseColor = new Color(args.accent).to('oklch')
75
-
76
- let accentScaleColors = getScaleFromColor(
77
- accentBaseColor,
78
- allScales,
79
- backgroundColor
80
- )
81
-
82
- // Enforce srgb for the background color
83
- const backgroundHex = backgroundColor.to('srgb').toString({ format: 'hex' })
84
-
85
- // Make sure we use the tint from the gray scale for when base is pure white or black
86
- const accentBaseHex = accentBaseColor.to('srgb').toString({ format: 'hex' })
87
- if (accentBaseHex === '#000' || accentBaseHex === '#fff') {
88
- accentScaleColors = grayScaleColors.map((color) =>
89
- color.clone()
90
- ) as ArrayOf12<Color>
91
- }
92
-
93
- const [accent9Color, accentContrastColor] = getStep9Colors(
94
- accentScaleColors,
95
- accentBaseColor
96
- )
97
-
98
- accentScaleColors[8] = accent9Color
99
- accentScaleColors[9] = getButtonHoverColor(accent9Color, [accentScaleColors])
100
-
101
- // Limit saturation of the text colors
102
- accentScaleColors[10].coords[1] = Math.min(
103
- Math.max(accentScaleColors[8].coords[1], accentScaleColors[7].coords[1]),
104
- accentScaleColors[10].coords[1]
105
- )
106
- accentScaleColors[11].coords[1] = Math.min(
107
- Math.max(accentScaleColors[8].coords[1], accentScaleColors[7].coords[1]),
108
- accentScaleColors[11].coords[1]
109
- )
110
-
111
- const accentScaleHex = accentScaleColors.map((color) =>
112
- color.to('srgb').toString({ format: 'hex' })
113
- ) as ArrayOf12<string>
114
-
115
- const accentScaleWideGamut = accentScaleColors.map(
116
- toOklchString
117
- ) as ArrayOf12<string>
118
-
119
- const accentScaleAlphaHex = accentScaleHex.map((color) =>
120
- getAlphaColorSrgb(color, backgroundHex)
121
- ) as ArrayOf12<string>
122
-
123
- const accentScaleAlphaWideGamutString = accentScaleHex.map((color) =>
124
- getAlphaColorP3(color, backgroundHex)
125
- ) as ArrayOf12<string>
126
-
127
- const accentContrastColorHex = accentContrastColor
128
- .to('srgb')
129
- .toString({ format: 'hex' })
130
-
131
- const grayScaleHex = grayScaleColors.map((color) =>
132
- color.to('srgb').toString({ format: 'hex' })
133
- ) as ArrayOf12<string>
134
-
135
- const grayScaleWideGamut = grayScaleColors.map(
136
- toOklchString
137
- ) as ArrayOf12<string>
138
-
139
- const grayScaleAlphaHex = grayScaleHex.map((color) =>
140
- getAlphaColorSrgb(color, backgroundHex)
141
- ) as ArrayOf12<string>
142
-
143
- const grayScaleAlphaWideGamutString = grayScaleHex.map((color) =>
144
- getAlphaColorP3(color, backgroundHex)
145
- ) as ArrayOf12<string>
146
-
147
- const accentSurfaceHex =
148
- appearance === 'light'
149
- ? getAlphaColorSrgb(accentScaleHex[1], backgroundHex, 0.8)
150
- : getAlphaColorSrgb(accentScaleHex[1], backgroundHex, 0.5)
151
-
152
- const accentSurfaceWideGamutString =
153
- appearance === 'light'
154
- ? getAlphaColorP3(accentScaleWideGamut[1], backgroundHex, 0.8)
155
- : getAlphaColorP3(accentScaleWideGamut[1], backgroundHex, 0.5)
156
-
157
- return {
158
- accentScale: accentScaleHex,
159
- accentScaleAlpha: accentScaleAlphaHex,
160
- accentScaleWideGamut: accentScaleWideGamut,
161
- accentScaleAlphaWideGamut: accentScaleAlphaWideGamutString,
162
- accentContrast: accentContrastColorHex,
163
-
164
- grayScale: grayScaleHex,
165
- grayScaleAlpha: grayScaleAlphaHex,
166
- grayScaleWideGamut: grayScaleWideGamut,
167
- grayScaleAlphaWideGamut: grayScaleAlphaWideGamutString,
168
-
169
- graySurface: appearance === 'light' ? '#ffffffcc' : 'rgba(0, 0, 0, 0.05)',
170
- graySurfaceWideGamut:
171
- appearance === 'light'
172
- ? 'color(display-p3 1 1 1 / 80%)'
173
- : 'color(display-p3 0 0 0 / 5%)',
174
-
175
- accentSurface: accentSurfaceHex,
176
- accentSurfaceWideGamut: accentSurfaceWideGamutString,
177
-
178
- background: backgroundHex,
179
- }
180
- }
181
-
182
- function getStep9Colors(
183
- scale: ArrayOf12<Color>,
184
- accentBaseColor: Color
185
- ): [Color, Color] {
186
- const referenceBackgroundColor = scale[0]
187
- const distance = accentBaseColor.deltaEOK(referenceBackgroundColor) * 100
188
-
189
- // If the accent base color is close to the page background color, it's likely
190
- // white on white or black on black, so we want to return something that makes sense instead
191
- if (distance < 25) {
192
- return [scale[8], getTextColor(scale[8])]
193
- }
194
-
195
- return [accentBaseColor, getTextColor(accentBaseColor)]
196
- }
197
-
198
- function getButtonHoverColor(source: Color, scales: ArrayOf12<Color>[]) {
199
- const [L, C, H] = source.coords
200
- const newL = L > 0.4 ? L - 0.03 / (L + 0.1) : L + 0.03 / (L + 0.1)
201
- const newC = L > 0.4 && !isNaN(H) ? C * 0.93 + 0 : C
202
- const buttonHoverColor = new Color('oklch', [newL, newC, H])
203
-
204
- // Find closest in-scale color to donate the chroma and hue.
205
- // Especially useful when the source color is pure white or black,
206
- // but the gray scale is tinted.
207
- let closestColor = buttonHoverColor
208
- let minDistance = Infinity
209
-
210
- scales.forEach((scale) => {
211
- for (const color of scale) {
212
- const distance = buttonHoverColor.deltaEOK(color)
213
- if (distance < minDistance) {
214
- minDistance = distance
215
- closestColor = color
216
- }
217
- }
218
- })
219
-
220
- buttonHoverColor.coords[1] = closestColor.coords[1]
221
- buttonHoverColor.coords[2] = closestColor.coords[2]
222
- return buttonHoverColor
223
- }
224
-
225
- function getScaleFromColor(
226
- source: Color,
227
- scales: Record<string, ArrayOf12<Color>>,
228
- backgroundColor: Color
229
- ) {
230
- let allColors: { scale: string; color: Color; distance: number }[] = []
231
-
232
- Object.entries(scales).forEach(([name, scale]) => {
233
- for (const color of scale) {
234
- const distance = source.deltaEOK(color)
235
- allColors.push({ scale: name, distance, color })
236
- }
237
- })
238
-
239
- allColors.sort((a, b) => a.distance - b.distance)
240
-
241
- // Remove non-unique scales
242
- let closestColors = allColors.filter(
243
- (color, i, arr) =>
244
- i === arr.findIndex((value) => value.scale === color.scale)
245
- )
246
-
247
- // If the next two closest colors are both grays, remove the second one until it’s not a gray anymore.
248
- // This is because up next we will be comparing how close the two closest colors are to the source color,
249
- // and since the grays are all extremely close to each other, we won’t get any useful data from the second
250
- // closest color if it’s also a gray.
251
- const grayScaleNamesStr = grayScaleNames as readonly string[]
252
- const allAreGrays = closestColors.every((color) =>
253
- grayScaleNamesStr.includes(color.scale)
254
- )
255
- if (!allAreGrays && grayScaleNamesStr.includes(closestColors[0].scale)) {
256
- while (grayScaleNamesStr.includes(closestColors[1].scale)) {
257
- closestColors.splice(1, 1)
258
- }
259
- }
260
-
261
- let colorA = closestColors[0]
262
- let colorB = closestColors[1]
263
-
264
- // Light trigonometry ahead.
265
- //
266
- // We want to determine the color that is the closest to the source color. Sometimes it makes sense
267
- // to proportionally mix the two closest colors together, but sometimes it is not useful at all.
268
- // Color coords are spatial in 3D, however we can treat the data we have as a 2D projection that is good enough.
269
- //
270
- // Case 1:
271
- // If the distances between the source color, the 1st closest color (A) and the 2nd closest color (B) form
272
- // a triangle where NEITHER angle A nor B are larger than 90 degrees, then we want to mix the 1st and the 2nd
273
- // closest colors in the same proportion as distances AD and BD are to each other. Mixing the two would result
274
- // in a color that would be closer to the source color than either of the two original closest colors.
275
- // Example: source color is a desaturated blue, which is between "indigo" and "slate" scales.
276
- //
277
- // C ← Source color
278
- // /|⟍
279
- // / | ⟍
280
- // b / | ⟍ a
281
- // / | ⟍
282
- // / | ⟍
283
- // A --- D -------- B
284
- // ↑
285
- // The color we want to use as the base, which is a mix of A and B.
286
- //
287
- // Case 2:
288
- // If the distances between the source color, the 1st closest color (A) and the 2nd closest color (B) form
289
- // a triangle where EITHER angle A or B are larger than 90 degrees, then we don’t care about point B because it’s
290
- // directionally the same as A, as mixing A and B can’t provide us with a color that is any closer to the source.
291
- // Example: source color is a saturated blue, with "blue" being the closest scale, and "indigo" just being further.
292
- //
293
- // C ← Source color
294
- // \⟍
295
- // \ ⟍
296
- // \ ⟍ a
297
- // b \ ⟍
298
- // \ ⟍
299
- // A ------- B
300
- // ↑
301
- // The color we want to use as the base, which is not influenced by B.
302
-
303
- // We’ll need all the lengths of the triangle sides, named after the angles they look at:
304
- const a = colorB.distance
305
- const b = colorA.distance
306
- const c = colorA.color.deltaEOK(colorB.color)
307
-
308
- // We can get the ratios of AD to BD lengths with trigonometry using tangents,
309
- // as the ratio of the tangents of the opposite angles will match.
310
- const cosA = (b ** 2 + c ** 2 - a ** 2) / (2 * b * c)
311
- const radA = Math.acos(cosA)
312
- const sinA = Math.sin(radA)
313
-
314
- const cosB = (a ** 2 + c ** 2 - b ** 2) / (2 * a * c)
315
- const radB = Math.acos(cosB)
316
- const sinB = Math.sin(radB)
317
-
318
- // Tangent of angle C in the ACD triangle
319
- const tanC1 = cosA / sinA
320
-
321
- // Tangent of angle C in the BCD triangle
322
- const tanC2 = cosB / sinB
323
-
324
- // The ratio of the tangents corresponds to the ratio of the distances AD to BD
325
- // In the end, it means how much of scale B we want to mix into scale A.
326
- // If it’s "0" or less, this is an obtuse triangle from case 2, and we use just scale A.
327
- const ratio = Math.max(0, tanC1 / tanC2) * 0.5
328
-
329
- // The base scale is going to be a mix of the two closest scales, with the mix ratio we determined before
330
- const scaleA = scales[colorA.scale]
331
- const scaleB = scales[colorB.scale]
332
- const scale = arrayOf12.map((i) =>
333
- new Color(Color.mix(scaleA[i], scaleB[i], ratio)).to('oklch')
334
- ) as ArrayOf12<Color>
335
-
336
- // Get the closest color from the pre-mixed scale we created
337
- const baseColor = scale
338
- .slice()
339
- .sort((a, b) => source.deltaEOK(a) - source.deltaEOK(b))[0]
340
-
341
- // Note the chroma difference between the source color and the base color
342
- const ratioC = source.coords[1] / baseColor.coords[1]
343
-
344
- // Modify hue and chroma of the scale to match the source color
345
- scale.forEach((color) => {
346
- color.coords[1] = Math.min(source.coords[1] * 1.5, color.coords[1] * ratioC)
347
- color.coords[2] = source.coords[2]
348
- })
349
-
350
- // Light mode
351
- if (scale[0].coords[0] > 0.5) {
352
- const lightnessScale = scale.map(({ coords }) => coords[0])
353
- const backgroundL = Math.max(0, Math.min(1, backgroundColor.coords[0]))
354
- const newLightnessScale = transposeProgressionStart(
355
- backgroundL,
356
- // Add white as the first "step" of the light scale
357
- [1, ...lightnessScale],
358
- lightModeEasing
359
- )
360
-
361
- // Remove the step we added
362
- newLightnessScale.shift()
363
-
364
- newLightnessScale.forEach((lightness, i) => {
365
- scale[i].coords[0] = lightness
366
- })
367
-
368
- return scale
369
- }
370
-
371
- // Dark mode
372
- let ease: typeof darkModeEasing = [...darkModeEasing]
373
- const referenceBackgroundColorL = scale[0].coords[0]
374
- const backgroundColorL = Math.max(0, Math.min(1, backgroundColor.coords[0]))
375
-
376
- // If background is lighter than step 0, we want to gradually change the easing to linear
377
- const ratioL = backgroundColorL / referenceBackgroundColorL
378
-
379
- if (ratioL > 1) {
380
- const maxRatio = 1.5
381
-
382
- for (let i = 0; i < ease.length; i++) {
383
- const metaRatio = (ratioL - 1) * (maxRatio / (maxRatio - 1))
384
- ease[i] = ratioL > maxRatio ? 0 : Math.max(0, ease[i] * (1 - metaRatio))
385
- }
386
- }
387
-
388
- const lightnessScale = scale.map(({ coords }) => coords[0])
389
- const backgroundL = backgroundColor.coords[0]
390
- const newLightnessScale = transposeProgressionStart(
391
- backgroundL,
392
- lightnessScale,
393
- ease
394
- )
395
-
396
- newLightnessScale.forEach((lightness, i) => {
397
- scale[i].coords[0] = lightness
398
- })
399
-
400
- return scale
401
- }
402
-
403
- function getTextColor(background: Color) {
404
- const white = new Color('oklch', [1, 0, 0])
405
-
406
- if (Math.abs(white.contrastAPCA(background)) < 40) {
407
- const [C, H] = background.coords
408
- return new Color('oklch', [0.25, Math.max(0.08 * C, 0.04), H])
409
- }
410
-
411
- return white
412
- }
413
-
414
- // target = background * (1 - alpha) + foreground * alpha
415
- // alpha = (target - background) / (foreground - background)
416
- // Expects 0-1 numbers for the RGB channels
417
- function getAlphaColor(
418
- targetRgb: number[],
419
- backgroundRgb: number[],
420
- rgbPrecision: number,
421
- alphaPrecision: number,
422
- targetAlpha?: number
423
- ) {
424
- const [tr, tg, tb] = targetRgb.map((c) => Math.round(c * rgbPrecision))
425
- const [br, bg, bb] = backgroundRgb.map((c) => Math.round(c * rgbPrecision))
426
-
427
- if (
428
- tr === undefined ||
429
- tg === undefined ||
430
- tb === undefined ||
431
- br === undefined ||
432
- bg === undefined ||
433
- bb === undefined
434
- ) {
435
- throw Error('Color is undefined')
436
- }
437
-
438
- // Is the background color lighter, RGB-wise, than target color?
439
- // Decide whether we want to add as little color or as much color as possible,
440
- // darkening or lightening the background respectively.
441
- // If at least one of the bits of the target RGB value
442
- // is lighter than the background, we want to lighten it.
443
- let desiredRgb = 0
444
- if (tr > br) {
445
- desiredRgb = rgbPrecision
446
- } else if (tg > bg) {
447
- desiredRgb = rgbPrecision
448
- } else if (tb > bb) {
449
- desiredRgb = rgbPrecision
450
- }
451
-
452
- const alphaR = (tr - br) / (desiredRgb - br)
453
- const alphaG = (tg - bg) / (desiredRgb - bg)
454
- const alphaB = (tb - bb) / (desiredRgb - bb)
455
-
456
- const isPureGray = [alphaR, alphaG, alphaB].every((alpha) => alpha === alphaR)
457
-
458
- // No need for precision gymnastics with pure grays, and we can get cleaner output
459
- if (!targetAlpha && isPureGray) {
460
- // Convert back to 0-1 values
461
- const V = desiredRgb / rgbPrecision
462
- return [V, V, V, alphaR] as const
463
- }
464
-
465
- const clampRgb = (n: number) =>
466
- isNaN(n) ? 0 : Math.min(rgbPrecision, Math.max(0, n))
467
- const clampA = (n: number) =>
468
- isNaN(n) ? 0 : Math.min(alphaPrecision, Math.max(0, n))
469
- const maxAlpha = targetAlpha ?? Math.max(alphaR, alphaG, alphaB)
470
-
471
- const A = clampA(Math.ceil(maxAlpha * alphaPrecision)) / alphaPrecision
472
- let R = clampRgb(((br * (1 - A) - tr) / A) * -1)
473
- let G = clampRgb(((bg * (1 - A) - tg) / A) * -1)
474
- let B = clampRgb(((bb * (1 - A) - tb) / A) * -1)
475
-
476
- R = Math.ceil(R)
477
- G = Math.ceil(G)
478
- B = Math.ceil(B)
479
-
480
- const blendedR = blendAlpha(R, A, br)
481
- const blendedG = blendAlpha(G, A, bg)
482
- const blendedB = blendAlpha(B, A, bb)
483
-
484
- // Correct for rounding errors in light mode
485
- if (desiredRgb === 0) {
486
- if (tr <= br && tr !== blendedR) {
487
- R = tr > blendedR ? R + 1 : R - 1
488
- }
489
-
490
- if (tg <= bg && tg !== blendedG) {
491
- G = tg > blendedG ? G + 1 : G - 1
492
- }
493
-
494
- if (tb <= bb && tb !== blendedB) {
495
- B = tb > blendedB ? B + 1 : B - 1
496
- }
497
- }
498
-
499
- // Correct for rounding errors in dark mode
500
- if (desiredRgb === rgbPrecision) {
501
- if (tr >= br && tr !== blendedR) {
502
- R = tr > blendedR ? R + 1 : R - 1
503
- }
504
-
505
- if (tg >= bg && tg !== blendedG) {
506
- G = tg > blendedG ? G + 1 : G - 1
507
- }
508
-
509
- if (tb >= bb && tb !== blendedB) {
510
- B = tb > blendedB ? B + 1 : B - 1
511
- }
512
- }
513
-
514
- // Convert back to 0-1 values
515
- R = R / rgbPrecision
516
- G = G / rgbPrecision
517
- B = B / rgbPrecision
518
-
519
- return [R, G, B, A] as const
520
- }
521
-
522
- // Important – I empirically discovered that this rounding is how the browser actually overlays
523
- // transparent RGB bits over each other. It does NOT round the whole result altogether.
524
- function blendAlpha(
525
- foreground: number,
526
- alpha: number,
527
- background: number,
528
- round = true
529
- ) {
530
- if (round) {
531
- return Math.round(background * (1 - alpha)) + Math.round(foreground * alpha)
532
- }
533
-
534
- return background * (1 - alpha) + foreground * alpha
535
- }
536
-
537
- function getAlphaColorSrgb(
538
- targetColor: string,
539
- backgroundColor: string,
540
- targetAlpha?: number
541
- ) {
542
- const [r, g, b, a] = getAlphaColor(
543
- new Color(targetColor).to('srgb').coords,
544
- new Color(backgroundColor).to('srgb').coords,
545
- 255,
546
- 255,
547
- targetAlpha
548
- )
549
-
550
- return formatHex(new Color('srgb', [r, g, b], a).toString({ format: 'hex' }))
551
- }
552
-
553
- function getAlphaColorP3(
554
- targetColor: string,
555
- backgroundColor: string,
556
- targetAlpha?: number
557
- ) {
558
- const [r, g, b, a] = getAlphaColor(
559
- new Color(targetColor).to('p3').coords,
560
- new Color(backgroundColor).to('p3').coords,
561
- // Not sure why, but the resulting P3 alpha colors are blended in the browser most precisely when
562
- // rounded to 255 integers too. Is the browser using 0-255 rather than 0-1 under the hood for P3 too?
563
- 255,
564
- 1000,
565
- targetAlpha
566
- )
567
-
568
- return (
569
- new Color('p3', [r, g, b], a)
570
- .toString({ precision: 4 })
571
- // Important: in non-browser environments colorjs.io outputs a different format for some reason
572
- .replace('color(p3 ', 'color(display-p3 ')
573
- )
574
- }
575
-
576
- // Format shortform hex to longform
577
- function formatHex(str: string) {
578
- if (!str.startsWith('#')) {
579
- return str
580
- }
581
-
582
- if (str.length === 4) {
583
- const hash = str.charAt(0)
584
- const r = str.charAt(1)
585
- const g = str.charAt(2)
586
- const b = str.charAt(3)
587
- return hash + r + r + g + g + b + b
588
- }
589
-
590
- if (str.length === 5) {
591
- const hash = str.charAt(0)
592
- const r = str.charAt(1)
593
- const g = str.charAt(2)
594
- const b = str.charAt(3)
595
- const a = str.charAt(4)
596
- return hash + r + r + g + g + b + b + a + a
597
- }
598
-
599
- return str
600
- }
601
-
602
- const darkModeEasing = [1, 0, 1, 0] as [number, number, number, number]
603
- const lightModeEasing = [0, 2, 0, 2] as [number, number, number, number]
604
-
605
- export function transposeProgressionStart(
606
- to: number,
607
- arr: number[],
608
- curve: [number, number, number, number]
609
- ) {
610
- return arr.map((n, i, arr) => {
611
- const lastIndex = arr.length - 1
612
- const diff = arr[0] - to
613
- const fn = BezierEasing(...curve)
614
- return n - diff * fn(1 - i / lastIndex)
615
- })
616
- }
617
-
618
- export function transposeProgressionEnd(
619
- to: number,
620
- arr: number[],
621
- curve: [number, number, number, number]
622
- ) {
623
- return arr.map((n, i, arr) => {
624
- const lastIndex = arr.length - 1
625
- const diff = arr[lastIndex] - to
626
- const fn = BezierEasing(...curve)
627
- return n - diff * fn(i / lastIndex)
628
- })
629
- }
630
-
631
- // Convert to OKLCH string with percentage for the lightness channel
632
- // https://github.com/radix-ui/themes/issues/420
633
- function toOklchString(color: Color) {
634
- const L = +(color.coords[0] * 100).toFixed(1)
635
- return color
636
- .to('oklch')
637
- .toString({ precision: 4 })
638
- .replace(/(\S+)(.+)/, `oklch(${L}%$2`)
639
- }