@asafarim/shared-i18n 0.8.0 → 0.9.0

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 (94) hide show
  1. package/README.md +266 -280
  2. package/demo/README.md +119 -0
  3. package/demo/index.html +12 -1
  4. package/demo/node_modules/.bin/kill-port +17 -0
  5. package/demo/node_modules/.bin/tsc +5 -9
  6. package/demo/node_modules/.bin/tsserver +5 -9
  7. package/demo/node_modules/.bin/vite +5 -9
  8. package/demo/package.json +7 -4
  9. package/demo/public/404.html +24 -0
  10. package/demo/public/favicon.svg +4 -4
  11. package/demo/public/logo.svg +24 -24
  12. package/demo/src/App.tsx +178 -129
  13. package/demo/src/components/CountryLanguageSelectorsPage.tsx +240 -0
  14. package/demo/src/components/GetStartedSection.tsx +56 -56
  15. package/demo/src/components/KeyTable.tsx +29 -29
  16. package/demo/src/components/LanguageBar.tsx +145 -103
  17. package/demo/src/components/LanguageSwitcherDemo.module.css +114 -114
  18. package/demo/src/components/LanguageSwitchersPage.tsx +245 -0
  19. package/demo/src/components/Logo.tsx +6 -6
  20. package/demo/src/components/OverviewSection.tsx +58 -43
  21. package/demo/src/components/Panel.tsx +15 -15
  22. package/demo/src/components/RoutingLabPage.tsx +147 -0
  23. package/demo/src/components/StatusCard.tsx +109 -109
  24. package/demo/src/data/countries.ts +48 -0
  25. package/demo/src/i18n/localeAdapter.ts +91 -0
  26. package/demo/src/i18n/localeRouting.ts +77 -0
  27. package/demo/src/index.css +1075 -644
  28. package/demo/src/locales/de/demo.json +202 -84
  29. package/demo/src/locales/en/demo.json +201 -85
  30. package/demo/src/locales/fr/demo.json +203 -85
  31. package/demo/src/locales/it/demo.json +202 -84
  32. package/demo/src/locales/lb/demo.json +201 -0
  33. package/demo/src/locales/nl/demo.json +203 -85
  34. package/demo/src/main.tsx +32 -29
  35. package/demo/tsconfig.json +18 -18
  36. package/demo/tsconfig.node.json +10 -10
  37. package/demo/tsconfig.tsbuildinfo +1 -1
  38. package/demo/vite-env.d.ts +7 -7
  39. package/demo/vite.config.d.ts +2 -2
  40. package/demo/vite.config.js +10 -10
  41. package/dist/components/LanguageSwitcher.module.css +303 -303
  42. package/dist/country-language-selector.css +431 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +2 -0
  46. package/dist/tsconfig.tsbuildinfo +1 -1
  47. package/package.json +87 -85
  48. package/demo/dist/Icon Dropdown_Limited Languages.png +0 -0
  49. package/demo/dist/Select Dropdown_Text Only.png +0 -0
  50. package/demo/dist/assets/favicon-BZYZvBLo.svg +0 -4
  51. package/demo/dist/assets/index-BdjqKw_N.css +0 -1
  52. package/demo/dist/assets/index-C1Tq1uEr.js +0 -191
  53. package/demo/dist/favicon.svg +0 -4
  54. package/demo/dist/index.html +0 -27
  55. package/demo/dist/logo.svg +0 -24
  56. package/demo/node_modules/.bin/browserslist +0 -21
  57. package/demo/node_modules/.bin/browserslist.CMD +0 -12
  58. package/demo/node_modules/.bin/browserslist.ps1 +0 -41
  59. package/demo/node_modules/.bin/tsc.CMD +0 -12
  60. package/demo/node_modules/.bin/tsc.ps1 +0 -41
  61. package/demo/node_modules/.bin/tsserver.CMD +0 -12
  62. package/demo/node_modules/.bin/tsserver.ps1 +0 -41
  63. package/demo/node_modules/.bin/vite.CMD +0 -12
  64. package/demo/node_modules/.bin/vite.ps1 +0 -41
  65. package/demo/node_modules/.vite/deps/@asafarim_country-language-selector.js +0 -848
  66. package/demo/node_modules/.vite/deps/@asafarim_country-language-selector.js.map +0 -7
  67. package/demo/node_modules/.vite/deps/_metadata.json +0 -76
  68. package/demo/node_modules/.vite/deps/chunk-5WRI5ZAA.js +0 -30
  69. package/demo/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +0 -7
  70. package/demo/node_modules/.vite/deps/chunk-B3AHR5EX.js +0 -1004
  71. package/demo/node_modules/.vite/deps/chunk-B3AHR5EX.js.map +0 -7
  72. package/demo/node_modules/.vite/deps/chunk-E6BG6WAU.js +0 -292
  73. package/demo/node_modules/.vite/deps/chunk-E6BG6WAU.js.map +0 -7
  74. package/demo/node_modules/.vite/deps/chunk-MVARZQEG.js +0 -280
  75. package/demo/node_modules/.vite/deps/chunk-MVARZQEG.js.map +0 -7
  76. package/demo/node_modules/.vite/deps/i18next-browser-languagedetector.js +0 -400
  77. package/demo/node_modules/.vite/deps/i18next-browser-languagedetector.js.map +0 -7
  78. package/demo/node_modules/.vite/deps/i18next.js +0 -2392
  79. package/demo/node_modules/.vite/deps/i18next.js.map +0 -7
  80. package/demo/node_modules/.vite/deps/package.json +0 -3
  81. package/demo/node_modules/.vite/deps/react-dom.js +0 -6
  82. package/demo/node_modules/.vite/deps/react-dom.js.map +0 -7
  83. package/demo/node_modules/.vite/deps/react-dom_client.js +0 -20217
  84. package/demo/node_modules/.vite/deps/react-dom_client.js.map +0 -7
  85. package/demo/node_modules/.vite/deps/react-i18next.js +0 -869
  86. package/demo/node_modules/.vite/deps/react-i18next.js.map +0 -7
  87. package/demo/node_modules/.vite/deps/react.js +0 -5
  88. package/demo/node_modules/.vite/deps/react.js.map +0 -7
  89. package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js +0 -278
  90. package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +0 -7
  91. package/demo/node_modules/.vite/deps/react_jsx-runtime.js +0 -6
  92. package/demo/node_modules/.vite/deps/react_jsx-runtime.js.map +0 -7
  93. package/demo/src/components/CountryLanguageDemo.tsx +0 -140
  94. package/demo/src/components/LanguageSwitcherDemo.tsx +0 -256
@@ -1,29 +1,29 @@
1
- import { useTranslation } from '@asafarim/shared-i18n'
2
-
3
- interface KeyTableProps {
4
- namespace: string
5
- keys: string[]
6
- }
7
-
8
- export default function KeyTable({ namespace, keys }: KeyTableProps) {
9
- const { t } = useTranslation(namespace)
10
-
11
- return (
12
- <table className="key-table">
13
- <thead>
14
- <tr>
15
- <th>Key</th>
16
- <th>Value</th>
17
- </tr>
18
- </thead>
19
- <tbody>
20
- {keys.map((key) => (
21
- <tr key={key}>
22
- <td>{key}</td>
23
- <td>{t(key)}</td>
24
- </tr>
25
- ))}
26
- </tbody>
27
- </table>
28
- )
29
- }
1
+ import { useTranslation } from '@asafarim/shared-i18n'
2
+
3
+ interface KeyTableProps {
4
+ namespace: string
5
+ keys: string[]
6
+ }
7
+
8
+ export default function KeyTable({ namespace, keys }: KeyTableProps) {
9
+ const { t } = useTranslation(namespace)
10
+
11
+ return (
12
+ <table className="key-table">
13
+ <thead>
14
+ <tr>
15
+ <th>Key</th>
16
+ <th>Value</th>
17
+ </tr>
18
+ </thead>
19
+ <tbody>
20
+ {keys.map((key) => (
21
+ <tr key={key}>
22
+ <td>{key}</td>
23
+ <td>{t(key)}</td>
24
+ </tr>
25
+ ))}
26
+ </tbody>
27
+ </table>
28
+ )
29
+ }
@@ -1,103 +1,145 @@
1
- import { CountryLanguageSelector } from '@asafarim/country-language-selector'
2
- import { useTranslation } from '@asafarim/shared-i18n'
3
- import { useState } from 'react'
4
-
5
- export default function LanguageBar() {
6
- const [isDarkTheme, setIsDarkTheme] = useState(false)
7
- const { i18n } = useTranslation()
8
-
9
- const toggleTheme = () => {
10
- setIsDarkTheme(!isDarkTheme)
11
- document.documentElement.setAttribute('data-theme', isDarkTheme ? 'light' : 'dark')
12
- }
13
-
14
- const handleLocaleChange = (locale: { country: string; language: string }) => {
15
- if (i18n?.changeLanguage) {
16
- i18n.changeLanguage(locale.language)
17
- }
18
- }
19
-
20
- const countries = [
21
- {
22
- code: 'BE',
23
- name: 'Belgium',
24
- nativeName: 'België',
25
- flag: '🇧🇪',
26
- languages: [
27
- { code: 'nl', label: 'Dutch', nativeLabel: 'Nederlands' },
28
- { code: 'fr', label: 'French', nativeLabel: 'Français' },
29
- { code: 'de', label: 'German', nativeLabel: 'Deutsch' }
30
- ]
31
- },
32
- {
33
- code: 'CH',
34
- name: 'Switzerland',
35
- nativeName: 'Schweiz',
36
- flag: '🇨🇭',
37
- languages: [
38
- { code: 'de', label: 'German', nativeLabel: 'Deutsch' },
39
- { code: 'fr', label: 'French', nativeLabel: 'Français' },
40
- { code: 'it', label: 'Italian', nativeLabel: 'Italiano' },
41
- { code: 'rm', label: 'Romansh', nativeLabel: 'Rumantsch' }
42
- ]
43
- },
44
- {
45
- code: 'CA',
46
- name: 'Canada',
47
- nativeName: 'Canada',
48
- flag: '🇨🇦',
49
- languages: [
50
- { code: 'en', label: 'English' },
51
- { code: 'fr', label: 'French', nativeLabel: 'Français' }
52
- ]
53
- }
54
- ]
55
-
56
- return (
57
- <div className="language-bar">
58
- <CountryLanguageSelector
59
- countries={countries}
60
- defaultValue={{ country: 'BE', language: 'nl' }}
61
- persistKey="demo-locale"
62
- triggerVariant="compact"
63
- onChange={handleLocaleChange}
64
- />
65
- <div className='package-source-actions'>
66
- <a
67
- href="https://github.com/AliSafari-IT/shared-i18n"
68
- target="_blank"
69
- rel="noopener noreferrer"
70
- className="icon-button"
71
- title="View on GitHub"
72
- aria-label="View repository on GitHub"
73
- >
74
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
75
- <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
76
- </svg>
77
- </a>
78
-
79
- <a
80
- href="https://www.npmjs.com/package/@asafarim/shared-i18n"
81
- target="_blank"
82
- rel="noopener noreferrer"
83
- className="icon-button"
84
- title="View on NPM"
85
- aria-label="View package on NPM"
86
- >
87
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
88
- <path d="M0 0v16h16v-16h-16zm8.5 9.5h-1v-3h1v3zm0-4h-1v-1h1v1zm-3.5 4h-1v-6h1v6zm-3.5 0h-1v-4h1v4zm10.5 0h-1v-2h1v2zm0-3h-1v-3h1v3z"/>
89
- </svg>
90
- </a>
91
-
92
- <button
93
- onClick={toggleTheme}
94
- className="icon-button"
95
- title={`Switch to ${isDarkTheme ? 'light' : 'dark'} theme`}
96
- aria-label={`Switch to ${isDarkTheme ? 'light' : 'dark'} theme`}
97
- >
98
- {isDarkTheme ? '☀️' : '🌙'}
99
- </button>
100
- </div>
101
- </div>
102
- )
103
- }
1
+ import { useEffect, useState } from 'react'
2
+ import { CountryLanguageSelector } from '@asafarim/shared-i18n'
3
+ import type { Locale } from '@asafarim/shared-i18n'
4
+ import { useTranslation } from '@asafarim/shared-i18n'
5
+ import { countries } from '../data/countries'
6
+ import type { DemoPage } from '../i18n/localeRouting'
7
+ import { PAGES, localeToSlug } from '../i18n/localeRouting'
8
+ import Logo from './Logo'
9
+
10
+ interface LanguageBarProps {
11
+ locale: Locale
12
+ onLocaleChange: (locale: Locale) => void
13
+ currentPage: DemoPage
14
+ onPageChange: (page: DemoPage) => void
15
+ }
16
+
17
+ const usePageLabels = (t: (key: string) => string): Record<DemoPage, string> => ({
18
+ 'overview': t('nav.overview'),
19
+ 'get-started': t('nav.getStarted'),
20
+ 'language-switchers': t('nav.languageSwitchers'),
21
+ 'country-language-selectors': t('nav.countrySelectors'),
22
+ 'routing-lab': t('nav.routingLab'),
23
+ })
24
+
25
+ function getStoredTheme(): 'light' | 'dark' {
26
+ if (typeof window === 'undefined') return 'light'
27
+ const saved = window.localStorage.getItem('demo-theme')
28
+ if (saved === 'light' || saved === 'dark') return saved
29
+ return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
30
+ }
31
+
32
+ export default function LanguageBar({ locale, onLocaleChange, currentPage, onPageChange }: LanguageBarProps) {
33
+ const { i18n, t } = useTranslation('demo')
34
+ const PAGE_LABELS = usePageLabels(t)
35
+ const [theme, setTheme] = useState<'light' | 'dark'>(() => getStoredTheme())
36
+
37
+ useEffect(() => {
38
+ document.documentElement.setAttribute('data-theme', theme)
39
+ window.localStorage.setItem('demo-theme', theme)
40
+ }, [theme])
41
+
42
+ const toggleTheme = () => setTheme(t => (t === 'dark' ? 'light' : 'dark'))
43
+
44
+ const handleLocaleChange = (next: Locale) => {
45
+ onLocaleChange(next)
46
+ if (i18n?.changeLanguage) i18n.changeLanguage(next.language)
47
+ }
48
+
49
+ return (
50
+ <header className="topnav" role="banner">
51
+ <div className="topnav__inner">
52
+ <a
53
+ className="topnav__brand"
54
+ href="#"
55
+ onClick={(e) => { e.preventDefault(); onPageChange('overview') }}
56
+ aria-label="Shared i18n home"
57
+ >
58
+ <Logo />
59
+ <div className="topnav__brand-text">
60
+ <span className="topnav__brand-name">@asafarim/shared-i18n</span>
61
+ <span className="topnav__brand-tag">{t('nav.tagline')}</span>
62
+ </div>
63
+ </a>
64
+
65
+ <nav className="topnav__pills" aria-label="Demo sections">
66
+ {PAGES.map(page => (
67
+ <button
68
+ key={page}
69
+ type="button"
70
+ className={`pill ${page === currentPage ? 'pill--active' : ''}`}
71
+ aria-current={page === currentPage ? 'page' : undefined}
72
+ onClick={() => onPageChange(page)}
73
+ >
74
+ {PAGE_LABELS[page]}
75
+ </button>
76
+ ))}
77
+ </nav>
78
+
79
+ <div className="topnav__actions">
80
+ <span className="locale-chip" title={`Active locale: ${localeToSlug(locale)}`}>
81
+ <span className="locale-chip__dot" aria-hidden="true" />
82
+ {locale.country} / {locale.language.toUpperCase()}
83
+ <span className="locale-chip__slug">{localeToSlug(locale)}</span>
84
+ </span>
85
+
86
+ <CountryLanguageSelector
87
+ countries={countries}
88
+ value={locale}
89
+ onChange={handleLocaleChange}
90
+ triggerVariant="compact"
91
+ flagMode="image"
92
+ align="end"
93
+ ariaLabel="Select country and language with image flags"
94
+ />
95
+
96
+ <CountryLanguageSelector
97
+ countries={countries}
98
+ value={locale}
99
+ onChange={handleLocaleChange}
100
+ triggerVariant="flag"
101
+ flagMode="image"
102
+ align="end"
103
+ ariaLabel="Select country and language with image flags"
104
+ />
105
+
106
+ <a
107
+ href="https://github.com/AliSafari-IT/shared-i18n"
108
+ target="_blank"
109
+ rel="noopener noreferrer"
110
+ className="icon-button"
111
+ title="View on GitHub"
112
+ aria-label="View repository on GitHub"
113
+ >
114
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
115
+ <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
116
+ </svg>
117
+ </a>
118
+
119
+ <a
120
+ href="https://www.npmjs.com/package/@asafarim/shared-i18n"
121
+ target="_blank"
122
+ rel="noopener noreferrer"
123
+ className="icon-button"
124
+ title="View on npm"
125
+ aria-label="View package on npm"
126
+ >
127
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
128
+ <path d="M0 0v16h16V0H0zm13 13h-2V6H8v7H3V3h10v10z"/>
129
+ </svg>
130
+ </a>
131
+
132
+ <button
133
+ type="button"
134
+ onClick={toggleTheme}
135
+ className="icon-button"
136
+ title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
137
+ aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
138
+ >
139
+ {theme === 'dark' ? '☀️' : '🌙'}
140
+ </button>
141
+ </div>
142
+ </div>
143
+ </header>
144
+ )
145
+ }
@@ -1,114 +1,114 @@
1
- @import "@asafarim/design-tokens/css";
2
-
3
- .container {
4
- display: flex;
5
- flex-direction: column;
6
- gap: var(--asm-space-6);
7
- overflow: visible;
8
- }
9
-
10
- .demoItem {
11
- border: 1px solid var(--asm-color-border);
12
- border-radius: var(--asm-radius-lg);
13
- overflow: visible;
14
- background: var(--asm-color-surface);
15
- transition: box-shadow var(--asm-motion-duration-fast) var(--asm-motion-easing-standard);
16
- }
17
-
18
- .demoItem:hover {
19
- box-shadow: var(--asm-effect-shadow-md);
20
- }
21
-
22
- .demoHeader {
23
- padding: var(--asm-space-4);
24
- border-bottom: 1px solid var(--asm-color-border);
25
- background: var(--asm-color-surface-muted);
26
- }
27
-
28
- .demoTitle {
29
- margin: 0 0 var(--asm-space-2) 0;
30
- font-size: var(--asm-font-size-sm);
31
- font-weight: 600;
32
- color: var(--asm-color-text);
33
- }
34
-
35
- .demoDescription {
36
- margin: 0;
37
- font-size: var(--asm-font-size-xs);
38
- color: var(--asm-color-text-muted);
39
- line-height: 1.5;
40
- }
41
-
42
- .demoContent {
43
- display: grid;
44
- grid-template-columns: 1fr 1fr;
45
- gap: 0;
46
- }
47
-
48
- .demoPreview {
49
- padding: var(--asm-space-4);
50
- border-right: 1px solid var(--asm-color-border);
51
- display: flex;
52
- flex-direction: column;
53
- gap: var(--asm-space-3);
54
- }
55
-
56
- .demoCode {
57
- padding: var(--asm-space-4);
58
- display: flex;
59
- flex-direction: column;
60
- gap: var(--asm-space-3);
61
- background: #fafafa;
62
- }
63
-
64
- .previewLabel,
65
- .codeLabel {
66
- font-size: var(--asm-font-size-xs);
67
- font-weight: 600;
68
- color: var(--asm-color-text-muted);
69
- text-transform: uppercase;
70
- letter-spacing: 0.5px;
71
- }
72
-
73
- .codeBlock {
74
- margin: 0;
75
- padding: var(--asm-space-3);
76
- background: var(--asm-color-surface);
77
- border: 1px solid var(--asm-color-border);
78
- border-radius: var(--asm-radius-md);
79
- font-size: 0.8rem;
80
- line-height: 1.6;
81
- color: var(--asm-color-text);
82
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
83
- overflow-x: auto;
84
- }
85
-
86
- @media (max-width: 1024px) {
87
- .demoContent {
88
- grid-template-columns: 1fr;
89
- }
90
-
91
- .demoPreview {
92
- border-right: none;
93
- border-bottom: 1px solid var(--asm-color-border);
94
- }
95
- }
96
-
97
- @media (max-width: 640px) {
98
- .demoItem {
99
- border-radius: var(--asm-radius-md);
100
- }
101
-
102
- .demoHeader {
103
- padding: var(--asm-space-3);
104
- }
105
-
106
- .demoPreview,
107
- .demoCode {
108
- padding: var(--asm-space-3);
109
- }
110
-
111
- .codeBlock {
112
- font-size: 0.75rem;
113
- }
114
- }
1
+ @import "@asafarim/design-tokens/css";
2
+
3
+ .container {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: var(--asm-space-6);
7
+ overflow: visible;
8
+ }
9
+
10
+ .demoItem {
11
+ border: 1px solid var(--asm-color-border);
12
+ border-radius: var(--asm-radius-lg);
13
+ overflow: visible;
14
+ background: var(--asm-color-surface);
15
+ transition: box-shadow var(--asm-motion-duration-fast) var(--asm-motion-easing-standard);
16
+ }
17
+
18
+ .demoItem:hover {
19
+ box-shadow: var(--asm-effect-shadow-md);
20
+ }
21
+
22
+ .demoHeader {
23
+ padding: var(--asm-space-4);
24
+ border-bottom: 1px solid var(--asm-color-border);
25
+ background: var(--asm-color-surface-muted);
26
+ }
27
+
28
+ .demoTitle {
29
+ margin: 0 0 var(--asm-space-2) 0;
30
+ font-size: var(--asm-font-size-sm);
31
+ font-weight: 600;
32
+ color: var(--asm-color-text);
33
+ }
34
+
35
+ .demoDescription {
36
+ margin: 0;
37
+ font-size: var(--asm-font-size-xs);
38
+ color: var(--asm-color-text-muted);
39
+ line-height: 1.5;
40
+ }
41
+
42
+ .demoContent {
43
+ display: grid;
44
+ grid-template-columns: 1fr 1fr;
45
+ gap: 0;
46
+ }
47
+
48
+ .demoPreview {
49
+ padding: var(--asm-space-4);
50
+ border-right: 1px solid var(--asm-color-border);
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: var(--asm-space-3);
54
+ }
55
+
56
+ .demoCode {
57
+ padding: var(--asm-space-4);
58
+ display: flex;
59
+ flex-direction: column;
60
+ gap: var(--asm-space-3);
61
+ background: #fafafa;
62
+ }
63
+
64
+ .previewLabel,
65
+ .codeLabel {
66
+ font-size: var(--asm-font-size-xs);
67
+ font-weight: 600;
68
+ color: var(--asm-color-text-muted);
69
+ text-transform: uppercase;
70
+ letter-spacing: 0.5px;
71
+ }
72
+
73
+ .codeBlock {
74
+ margin: 0;
75
+ padding: var(--asm-space-3);
76
+ background: var(--asm-color-surface);
77
+ border: 1px solid var(--asm-color-border);
78
+ border-radius: var(--asm-radius-md);
79
+ font-size: 0.8rem;
80
+ line-height: 1.6;
81
+ color: var(--asm-color-text);
82
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
83
+ overflow-x: auto;
84
+ }
85
+
86
+ @media (max-width: 1024px) {
87
+ .demoContent {
88
+ grid-template-columns: 1fr;
89
+ }
90
+
91
+ .demoPreview {
92
+ border-right: none;
93
+ border-bottom: 1px solid var(--asm-color-border);
94
+ }
95
+ }
96
+
97
+ @media (max-width: 640px) {
98
+ .demoItem {
99
+ border-radius: var(--asm-radius-md);
100
+ }
101
+
102
+ .demoHeader {
103
+ padding: var(--asm-space-3);
104
+ }
105
+
106
+ .demoPreview,
107
+ .demoCode {
108
+ padding: var(--asm-space-3);
109
+ }
110
+
111
+ .codeBlock {
112
+ font-size: 0.75rem;
113
+ }
114
+ }