@bookklik/senangstart-css 0.2.9 → 0.2.12

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 (69) hide show
  1. package/.agent/skills/add-utility/SKILL.md +65 -0
  2. package/.agent/workflows/add-utility.md +2 -0
  3. package/.agent/workflows/build.md +2 -0
  4. package/.agent/workflows/dev.md +2 -0
  5. package/AGENTS.md +30 -0
  6. package/dist/senangstart-css.js +607 -180
  7. package/dist/senangstart-css.min.js +234 -195
  8. package/dist/senangstart-tw.js +274 -8
  9. package/dist/senangstart-tw.min.js +1 -1
  10. package/docs/SYNTAX-REFERENCE.md +1731 -1590
  11. package/docs/guide/preflight.md +20 -1
  12. package/docs/ms/guide/preflight.md +19 -0
  13. package/docs/ms/reference/breakpoints.md +14 -0
  14. package/docs/ms/reference/visual/border-radius.md +50 -10
  15. package/docs/ms/reference/visual/contain.md +57 -0
  16. package/docs/ms/reference/visual/content-visibility.md +53 -0
  17. package/docs/ms/reference/visual/placeholder-color.md +92 -0
  18. package/docs/ms/reference/visual/ring-color.md +2 -2
  19. package/docs/ms/reference/visual/ring-offset.md +3 -3
  20. package/docs/ms/reference/visual/ring.md +5 -5
  21. package/docs/ms/reference/visual/writing-mode.md +53 -0
  22. package/docs/ms/reference/visual.md +6 -0
  23. package/docs/public/assets/senangstart-css.min.js +234 -195
  24. package/docs/public/llms.txt +45 -12
  25. package/docs/reference/breakpoints.md +14 -0
  26. package/docs/reference/visual/border-radius.md +50 -10
  27. package/docs/reference/visual/contain.md +57 -0
  28. package/docs/reference/visual/content-visibility.md +53 -0
  29. package/docs/reference/visual/placeholder-color.md +92 -0
  30. package/docs/reference/visual/ring-color.md +2 -2
  31. package/docs/reference/visual/ring-offset.md +3 -3
  32. package/docs/reference/visual/ring.md +5 -5
  33. package/docs/reference/visual/writing-mode.md +53 -0
  34. package/docs/reference/visual.md +7 -0
  35. package/docs/syntax-reference.json +2185 -2009
  36. package/package.json +1 -1
  37. package/scripts/convert-tailwind.js +300 -26
  38. package/scripts/generate-docs.js +403 -403
  39. package/src/cdn/senangstart-engine.js +5 -5
  40. package/src/cdn/tw-conversion-engine.js +305 -8
  41. package/src/cli/commands/build.js +51 -13
  42. package/src/cli/commands/dev.js +157 -93
  43. package/src/compiler/generators/css.js +467 -208
  44. package/src/compiler/generators/preflight.js +26 -13
  45. package/src/compiler/generators/typescript.js +3 -1
  46. package/src/compiler/index.js +27 -3
  47. package/src/compiler/parser.js +13 -6
  48. package/src/compiler/tokenizer.js +25 -23
  49. package/src/config/defaults.js +3 -0
  50. package/src/core/tokenizer-core.js +46 -19
  51. package/src/definitions/index.js +4 -1
  52. package/src/definitions/visual-borders.js +10 -10
  53. package/src/definitions/visual-performance.js +126 -0
  54. package/src/definitions/visual.js +25 -9
  55. package/src/utils/common.js +456 -27
  56. package/src/utils/node-io.js +82 -0
  57. package/tests/integration/dev-recovery.test.js +231 -0
  58. package/tests/unit/cli/memory-limits.test.js +169 -0
  59. package/tests/unit/compiler/css-generation-error-handling.test.js +204 -0
  60. package/tests/unit/compiler/generators/css-errors.test.js +102 -0
  61. package/tests/unit/compiler/generators/css.test.js +102 -5
  62. package/tests/unit/convert-tailwind.test.js +518 -431
  63. package/tests/unit/utils/common.test.js +376 -26
  64. package/tests/unit/utils/file-timeout.test.js +154 -0
  65. package/tests/unit/utils/theme-validation.test.js +181 -0
  66. package/tests/unit/compiler/generators/css.coverage.test.js +0 -833
  67. package/tests/unit/convert-tailwind.cli.test.js +0 -95
  68. package/tests/unit/security.test.js +0 -206
  69. /package/tests/unit/{convert-tailwind.coverage.test.js → convert-tailwind-edgecases.test.js} +0 -0
@@ -71,10 +71,13 @@ hr {
71
71
  }
72
72
 
73
73
  /*
74
- * Add the correct text decoration in Chrome, Edge, and Safari
74
+ * Set default placeholder color to a semi-transparent gray
75
+ * Uses theme variable for customization with fallback
75
76
  */
76
- abbr:where([title]) {
77
- text-decoration: underline dotted;
77
+ input::placeholder,
78
+ textarea::placeholder {
79
+ opacity: 1; /* 1 */
80
+ color: var(--placeholder-color, #9ca3af); /* 2 */
78
81
  }
79
82
 
80
83
  /*
@@ -217,10 +220,17 @@ input:where([type='submit']) {
217
220
  }
218
221
 
219
222
  /*
220
- * Add the correct vertical alignment in Chrome and Firefox
223
+ * Add the correct text decoration in Chrome, Edge, and Safari
221
224
  */
222
- progress {
223
- vertical-align: baseline;
225
+ abbr:where([title]) {
226
+ text-decoration: underline dotted;
227
+ }
228
+
229
+ /*
230
+ * Make sure links don't get underlined in headings
231
+ */
232
+ h1, h2, h3, h4, h5, h6 {
233
+ text-decoration: none;
224
234
  }
225
235
 
226
236
  /*
@@ -292,14 +302,17 @@ legend {
292
302
  }
293
303
 
294
304
  /*
295
- * Remove default list styles
305
+ * 1. Use a more sensible default box-sizing strategy
296
306
  */
297
- ol,
298
- ul,
299
- menu {
300
- list-style: none;
301
- margin: 0;
302
- padding: 0;
307
+ *,
308
+ ::before,
309
+ ::after {
310
+ box-sizing: border-box;
311
+ /* Support safe-area-inset for modern devices with notches */
312
+ padding-top: env(safe-area-inset-top);
313
+ padding-right: env(safe-area-inset-right);
314
+ padding-bottom: env(safe-area-inset-bottom);
315
+ padding-left: env(safe-area-inset-left);
303
316
  }
304
317
 
305
318
  /*
@@ -19,7 +19,9 @@ export function generateTypeScript(config) {
19
19
 
20
20
  // Generate radius unions
21
21
  const radiusKeys = Object.keys(theme.radius);
22
- const roundedUnions = radiusKeys.map(k => `'rounded:${k}'`).join(' | ');
22
+ const directions = ['t', 'b', 'l', 'r', 'tl', 'tr', 'bl', 'br'];
23
+ const roundedUnions = radiusKeys.map(k => `'rounded:${k}'`).join(' | ') +
24
+ ' | ' + directions.flatMap(d => radiusKeys.map(k => `'rounded-${d}:${k}'`)).join(' | ');
23
25
 
24
26
  // Generate shadow unions
25
27
  const shadowKeys = Object.keys(theme.shadow);
@@ -18,11 +18,24 @@ import { generateTypeScript } from './generators/typescript.js';
18
18
  export function compileSource(content, config) {
19
19
  const parsed = parseSource(content);
20
20
  const tokens = tokenizeAll(parsed);
21
+
22
+ // Check for invalid tokens and log warnings
23
+ const invalidTokens = tokens.filter(token => token.error);
24
+ if (invalidTokens.length > 0) {
25
+ if (typeof console !== 'undefined') {
26
+ console.warn(`\n${invalidTokens.length} error(s) found in source:`);
27
+ for (const token of invalidTokens) {
28
+ console.warn(` • ${token.raw} (${token.attrType}): ${token.error}`);
29
+ }
30
+ }
31
+ }
32
+
21
33
  const css = generateCSS(tokens, config);
22
34
 
23
35
  return {
24
36
  tokens,
25
37
  css,
38
+ errors: invalidTokens.length > 0 ? invalidTokens : null,
26
39
  minifiedCSS: config.output?.minify ? minifyCSS(css) : null
27
40
  };
28
41
  }
@@ -36,14 +49,25 @@ export function compileSource(content, config) {
36
49
  export function compileMultiple(files, config) {
37
50
  const parsed = parseMultipleSources(files);
38
51
  const tokens = tokenizeAll(parsed);
52
+
53
+ // Check for invalid tokens and log warnings
54
+ const invalidTokens = tokens.filter(token => token.error);
55
+ if (invalidTokens.length > 0) {
56
+ if (typeof console !== 'undefined') {
57
+ console.warn(`\n${invalidTokens.length} error(s) found in source:`);
58
+ for (const token of invalidTokens) {
59
+ console.warn(` • ${token.raw} (${token.attrType}): ${token.error}`);
60
+ }
61
+ }
62
+ }
63
+
39
64
  const css = generateCSS(tokens, config);
40
65
 
41
66
  return {
42
67
  tokens,
43
68
  css,
44
- minifiedCSS: config.output?.minify ? minifyCSS(css) : null,
45
- aiContext: generateAIContext(config),
46
- typescript: generateTypeScript(config)
69
+ errors: invalidTokens.length > 0 ? invalidTokens : null,
70
+ minifiedCSS: config.output?.minify ? minifyCSS(css) : null
47
71
  };
48
72
  }
49
73
 
@@ -18,11 +18,11 @@ const ATTRIBUTE_PATTERNS = {
18
18
  */
19
19
  function createAttributePatterns() {
20
20
  return {
21
- layout: /layout\s*=\s*["']([^"']*)["']/g,
22
- space: /space\s*=\s*["']([^"']*)["']/g,
23
- visual: /visual\s*=\s*["']([^"']*)["']/g,
24
- interact: /interact\s*=\s*["']([^"']*)["']/g,
25
- listens: /listens\s*=\s*["']([^"']*)["']/g
21
+ layout: /layout\s*=\s*("[^"]*"|'[^']*')/g,
22
+ space: /space\s*=\s*("[^"]*"|'[^']*')/g,
23
+ visual: /visual\s*=\s*("[^"]*"|'[^']*')/g,
24
+ interact: /interact\s*=\s*("[^"]*"|'[^']*')/g,
25
+ listens: /listens\s*=\s*("[^"]*"|'[^']*')/g
26
26
  };
27
27
  }
28
28
 
@@ -45,7 +45,14 @@ export function parseSource(content) {
45
45
  for (const [attr, pattern] of Object.entries(patterns)) {
46
46
  let match;
47
47
  while ((match = pattern.exec(content)) !== null) {
48
- const value = match[1].trim();
48
+ let value = match[1].trim();
49
+ if (value.length >= 2) {
50
+ const firstChar = value[0];
51
+ const lastChar = value[value.length - 1];
52
+ if ((firstChar === '"' && lastChar === '"') || (firstChar === "'" && lastChar === "'")) {
53
+ value = value.slice(1, -1);
54
+ }
55
+ }
49
56
  if (value.length > 10000) {
50
57
  continue;
51
58
  }
@@ -1,23 +1,25 @@
1
- /**
2
- * SenangStart CSS - Tokenizer
3
- * Re-exports core tokenizer functions with backward compatibility
4
- */
5
-
6
- // Re-export everything from core tokenizer
7
- export {
8
- tokenize,
9
- tokenizeAll,
10
- sanitizeValue,
11
- isValidToken
12
- } from '../core/tokenizer-core.js';
13
-
14
- // Re-export constants for backward compatibility
15
- export {
16
- BREAKPOINTS,
17
- STATES,
18
- LAYOUT_KEYWORDS
19
- } from '../core/constants.js';
20
-
21
- // Default export for backward compatibility
22
- import { tokenize, tokenizeAll } from '../core/tokenizer-core.js';
23
- export default { tokenize, tokenizeAll };
1
+ /**
2
+ * SenangStart CSS - Tokenizer
3
+ * Re-exports core tokenizer functions with backward compatibility
4
+ */
5
+
6
+ // Re-export everything from core tokenizer
7
+ export {
8
+ tokenize,
9
+ tokenizeAll,
10
+ tokenizeAllWithBatching,
11
+ sanitizeValue,
12
+ isValidToken
13
+ } from '../core/tokenizer-core.js';
14
+
15
+ // Re-export constants for backward compatibility
16
+ export {
17
+ BREAKPOINTS,
18
+ STATES,
19
+ LAYOUT_KEYWORDS
20
+ } from '../core/constants.js';
21
+
22
+ // Default export for backward compatibility
23
+ import { tokenize, tokenizeAll, tokenizeAllWithBatching } from '../core/tokenizer-core.js';
24
+ export default { tokenize, tokenizeAll, tokenizeAllWithBatching };
25
+
@@ -138,6 +138,7 @@ export const defaultConfig = {
138
138
  'tab': '768px', // Tablet
139
139
  'lap': '1024px', // Laptop
140
140
  'desk': '1280px', // Desktop
141
+ 'print': 'print', // Print media query
141
142
 
142
143
  // Tailwind Compatibility
143
144
  'tw-sm': '640px',
@@ -148,6 +149,8 @@ export const defaultConfig = {
148
149
  },
149
150
 
150
151
  // 7. COLORS: Palette Scales
152
+ // Placeholder color for form inputs
153
+ placeholder: '#9ca3af',
151
154
  colors: {
152
155
  // Base colors
153
156
  'white': '#FFFFFF',
@@ -1,10 +1,10 @@
1
- /**
2
- * SenangStart CSS - Core Tokenizer
3
- * Pure tokenizer functions shared by JIT runtime and build-time compiler
4
- */
5
-
6
- import { BREAKPOINTS, STATES, LAYOUT_KEYWORDS } from './constants.js';
7
- import { sanitizeValue } from '../utils/common.js';
1
+ /**
2
+ * SenangStart CSS - Core Tokenizer
3
+ * Pure tokenizer functions shared by JIT runtime and build-time compiler
4
+ */
5
+
6
+ import { BREAKPOINTS, STATES, LAYOUT_KEYWORDS } from './constants.js';
7
+ import { sanitizeValue, batchProcessTokens } from '../utils/common.js';
8
8
 
9
9
  /**
10
10
  * Sanitize token value to prevent CSS injection
@@ -164,15 +164,42 @@ export function tokenize(raw, attrType) {
164
164
  * @returns {Array} - Array of token objects
165
165
  */
166
166
  export function tokenizeAll(parsed) {
167
- const tokens = [];
168
-
169
- for (const [attrType, values] of Object.entries(parsed)) {
170
- for (const raw of values) {
171
- tokens.push(tokenize(raw, attrType));
172
- }
173
- }
174
-
175
- return tokens;
176
- }
177
-
178
- export default { tokenize, tokenizeAll, sanitizeValue, isValidToken };
167
+ const tokens = [];
168
+
169
+ for (const [attrType, values] of Object.entries(parsed)) {
170
+ for (const raw of values) {
171
+ tokens.push(tokenize(raw, attrType));
172
+ }
173
+ }
174
+
175
+ return tokens;
176
+ }
177
+
178
+ /**
179
+ * Tokenize all values with memory-protected batch processing
180
+ * @param {Object} parsed - Parsed tokens from parser { layout: Set, space: Set, visual: Set }
181
+ * @param {number} batchSize - Number of tokens per batch (default: 1000)
182
+ * @returns {Promise<Array>} - Array of token objects
183
+ */
184
+ export async function tokenizeAllWithBatching(parsed, batchSize = 1000) {
185
+ const rawTokens = [];
186
+
187
+ // Collect all raw tokens first
188
+ for (const [attrType, values] of Object.entries(parsed)) {
189
+ for (const raw of values) {
190
+ rawTokens.push({ raw, attrType });
191
+ }
192
+ }
193
+
194
+ // Process tokens in batches with memory protection
195
+ const tokens = await batchProcessTokens(
196
+ rawTokens,
197
+ ({ raw, attrType }) => tokenize(raw, attrType),
198
+ batchSize
199
+ );
200
+
201
+ return tokens;
202
+ }
203
+
204
+ export default { tokenize, tokenizeAll, tokenizeAllWithBatching, sanitizeValue, isValidToken };
205
+
@@ -16,6 +16,7 @@ import transformDefinitions from './visual-transforms.js';
16
16
  import borderDefinitions from './visual-borders.js';
17
17
  import divideDefinitions from './visual-divide.js';
18
18
  import svgDefinitions from './visual-svg.js';
19
+ import performanceDefinitions from './visual-performance.js';
19
20
 
20
21
  // Import split layout definitions
21
22
  import flexDefinitions from './layout-flex.js';
@@ -37,7 +38,8 @@ const allVisualDefinitions = {
37
38
  ...transformDefinitions,
38
39
  ...borderDefinitions,
39
40
  ...divideDefinitions,
40
- ...svgDefinitions
41
+ ...svgDefinitions,
42
+ ...performanceDefinitions
41
43
  };
42
44
 
43
45
  // Re-export all definitions
@@ -54,6 +56,7 @@ export { transformDefinitions } from './visual-transforms.js';
54
56
  export { borderDefinitions } from './visual-borders.js';
55
57
  export { divideDefinitions } from './visual-divide.js';
56
58
  export { svgDefinitions } from './visual-svg.js';
59
+ export { performanceDefinitions } from './visual-performance.js';
57
60
 
58
61
  // Re-export split layout definitions
59
62
  export { flexDefinitions } from './layout-flex.js';
@@ -181,11 +181,11 @@ export const ring = {
181
181
  supportsArbitrary: true,
182
182
  values: [
183
183
  { value: 'none', css: 'box-shadow: 0 0 0 0 transparent;', description: 'No ring', descriptionMs: 'Tiada cincin' },
184
- { value: 'thin', css: 'box-shadow: 0 0 0 1px var(--ring-color);', description: 'Thin ring (1px)', descriptionMs: 'Cincin nipis (1px)' },
185
- { value: 'regular', css: 'box-shadow: 0 0 0 2px var(--ring-color);', description: 'Regular ring (2px)', descriptionMs: 'Cincin biasa (2px)' },
186
- { value: 'small', css: 'box-shadow: 0 0 0 4px var(--ring-color);', description: 'Small ring (4px)', descriptionMs: 'Cincin kecil (4px)' },
187
- { value: 'medium', css: 'box-shadow: 0 0 0 6px var(--ring-color);', description: 'Medium ring (6px)', descriptionMs: 'Cincin sederhana (6px)' },
188
- { value: 'big', css: 'box-shadow: 0 0 0 8px var(--ring-color);', description: 'Big ring (8px)', descriptionMs: 'Cincin besar (8px)' }
184
+ { value: 'thin', css: 'box-shadow: var(--ring-inset) 0 0 0 1px var(--ss-ring-color);', description: 'Thin ring (1px)', descriptionMs: 'Cincin nipis (1px)' },
185
+ { value: 'regular', css: 'box-shadow: var(--ring-inset) 0 0 0 2px var(--ss-ring-color);', description: 'Regular ring (2px)', descriptionMs: 'Cincin biasa (2px)' },
186
+ { value: 'small', css: 'box-shadow: var(--ring-inset) 0 0 0 4px var(--ss-ring-color);', description: 'Small ring (4px)', descriptionMs: 'Cincin kecil (4px)' },
187
+ { value: 'medium', css: 'box-shadow: var(--ring-inset) 0 0 0 6px var(--ss-ring-color);', description: 'Medium ring (6px)', descriptionMs: 'Cincin sederhana (6px)' },
188
+ { value: 'big', css: 'box-shadow: var(--ring-inset) 0 0 0 8px var(--ss-ring-color);', description: 'Big ring (8px)', descriptionMs: 'Cincin besar (8px)' }
189
189
  ],
190
190
  examples: [
191
191
  { code: '<button visual="focus-visible:ring:small ring-color:primary">Focus me</button>', description: 'Focus ring on keyboard focus' },
@@ -216,8 +216,8 @@ export const ringColor = {
216
216
  usesScale: 'colors',
217
217
  supportsArbitrary: true,
218
218
  values: [
219
- { value: 'primary', css: '--ring-color: var(--c-primary);', description: 'Primary ring color', descriptionMs: 'Warna cincin utama' },
220
- { value: 'blue-500', css: '--ring-color: var(--c-blue-500);', description: 'Blue ring color', descriptionMs: 'Warna cincin biru' }
219
+ { value: 'primary', css: '--ss-ring-color: var(--c-primary);', description: 'Primary ring color', descriptionMs: 'Warna cincin utama' },
220
+ { value: 'blue-500', css: '--ss-ring-color: var(--c-blue-500);', description: 'Blue ring color', descriptionMs: 'Warna cincin biru' }
221
221
  ],
222
222
  examples: [
223
223
  { code: '<button visual="ring:small ring-color:primary">Colored ring</button>', description: 'Ring with custom color' }
@@ -233,9 +233,9 @@ export const ringOffset = {
233
233
  category: 'visual',
234
234
  supportsArbitrary: true,
235
235
  values: [
236
- { value: '0', css: '--ring-offset: 0px;', description: 'No offset', descriptionMs: 'Tiada ruang' },
237
- { value: '2', css: '--ring-offset: 2px;', description: '2px offset', descriptionMs: 'Ruang 2px' },
238
- { value: '4', css: '--ring-offset: 4px;', description: '4px offset', descriptionMs: 'Ruang 4px' }
236
+ { value: '0', css: '--ss-ring-offset-width: 0px;', description: 'No offset', descriptionMs: 'Tiada ruang' },
237
+ { value: '2', css: '--ss-ring-offset-width: 2px;', description: '2px offset', descriptionMs: 'Ruang 2px' },
238
+ { value: '4', css: '--ss-ring-offset-width: 4px;', description: '4px offset', descriptionMs: 'Ruang 4px' }
239
239
  ],
240
240
  examples: [
241
241
  { code: '<button visual="ring:small ring-offset:2 ring-color:primary">With offset</button>', description: 'Ring with offset' }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * SenangStart CSS - Performance Utility Definitions
3
+ * Content visibility, contain, and other performance optimizations
4
+ */
5
+
6
+ // ======================
7
+ // CONTENT VISIBILITY
8
+ // ======================
9
+
10
+ export const contentVisibility = {
11
+ name: 'content-visibility',
12
+ property: 'visual',
13
+ syntax: 'visual="content-visibility:[value]"',
14
+ description: 'Optimize rendering by skipping off-screen content',
15
+ descriptionMs: 'Optimumkan rendering dengan melangkau kandungan luar skrin',
16
+ category: 'visual',
17
+ values: [
18
+ { value: 'visible', css: 'content-visibility: visible;', description: 'Render all content', descriptionMs: 'Render semua kandungan' },
19
+ { value: 'auto', css: 'content-visibility: auto;', description: 'Skip when off-screen', descriptionMs: 'Langkau bila luar skrin' },
20
+ { value: 'hidden', css: 'content-visibility: hidden;', description: 'Never render off-screen', descriptionMs: 'Jangan render luar skrin' }
21
+ ],
22
+ examples: [
23
+ { code: '<section visual="content-visibility:auto">Large list</section>', description: 'Auto-optimize large content' },
24
+ { code: '<div visual="content-visibility:hidden">Hidden until needed</div>', description: 'Hide until revealed' }
25
+ ],
26
+ preview: [
27
+ {
28
+ title: 'Content Visibility',
29
+ titleMs: 'Ketampakan Kandungan',
30
+ description: 'Performance optimization for off-screen content',
31
+ descriptionMs: 'Pengoptimuman prestasi untuk kandungan luar skrin',
32
+ html: `<div layout="flex" space="g:medium p:medium" visual="bg:neutral-100 dark:bg:neutral-900 rounded:medium">
33
+ <div space="p:small" visual="bg:primary text:white rounded:small">visible</div>
34
+ <div space="p:small" visual="bg:primary text:white rounded:small">auto</div>
35
+ <div space="p:small" visual="bg:primary text:white rounded:small">hidden</div>
36
+ </div>`,
37
+ highlightValue: 'content-visibility:auto'
38
+ }
39
+ ]
40
+ };
41
+
42
+ // ======================
43
+ // CONTAIN
44
+ // ======================
45
+
46
+ export const contain = {
47
+ name: 'contain',
48
+ property: 'visual',
49
+ syntax: 'visual="contain:[value]"',
50
+ description: 'Isolate element rendering for performance',
51
+ descriptionMs: 'Pencil rendering elemen untuk prestasi',
52
+ category: 'visual',
53
+ values: [
54
+ { value: 'none', css: 'contain: none;', description: 'No containment', descriptionMs: 'Tiada pengandungan' },
55
+ { value: 'strict', css: 'contain: strict;', description: 'Full containment', descriptionMs: 'Pengandungan penuh' },
56
+ { value: 'content', css: 'contain: content;', description: 'Content containment', descriptionMs: 'Pengandungan kandungan' },
57
+ { value: 'size', css: 'contain: size;', description: 'Size containment', descriptionMs: 'Pengandungan saiz' },
58
+ { value: 'layout', css: 'contain: layout;', description: 'Layout containment', descriptionMs: 'Pengandungan susun atur' },
59
+ { value: 'style', css: 'contain: style;', description: 'Style containment', descriptionMs: 'Pengandungan gaya' },
60
+ { value: 'paint', css: 'contain: paint;', description: 'Paint containment', descriptionMs: 'Pengandungan lukis' }
61
+ ],
62
+ examples: [
63
+ { code: '<div visual="contain:strict">Isolated rendering</div>', description: 'Full containment' },
64
+ { code: '<div visual="contain:content">Content isolation</div>', description: 'Content only' }
65
+ ],
66
+ preview: [
67
+ {
68
+ title: 'Contain',
69
+ titleMs: 'Mengandung',
70
+ description: 'Isolate element from rest of page for performance',
71
+ descriptionMs: 'Pencil elemen dari halaman lain untuk prestasi',
72
+ html: `<div layout="flex" space="g:medium p:medium" visual="bg:neutral-100 dark:bg:neutral-900 rounded:medium">
73
+ <div space="p:small" visual="bg:primary text:white rounded:small">none</div>
74
+ <div space="p:small" visual="bg:primary text:white rounded:small">content</div>
75
+ <div space="p:small" visual="bg:primary text:white rounded:small">strict</div>
76
+ </div>`,
77
+ highlightValue: 'contain:strict'
78
+ }
79
+ ]
80
+ };
81
+
82
+ // ======================
83
+ // WRITING MODE
84
+ // ======================
85
+
86
+ export const writingMode = {
87
+ name: 'writing-mode',
88
+ property: 'visual',
89
+ syntax: 'visual="writing-mode:[value]"',
90
+ description: 'Set writing direction for RTL/vertical text',
91
+ descriptionMs: 'Tetapkan arah penulisan untuk teks RTL/menegak',
92
+ category: 'visual',
93
+ values: [
94
+ { value: 'horizontal-tb', css: 'writing-mode: horizontal-tb;', description: 'Left to right', descriptionMs: 'Kiri ke kanan' },
95
+ { value: 'vertical-rl', css: 'writing-mode: vertical-rl;', description: 'Top to bottom RTL', descriptionMs: 'Atas ke bawah RTL' },
96
+ { value: 'vertical-lr', css: 'writing-mode: vertical-lr;', description: 'Top to bottom LTR', descriptionMs: 'Atas ke bawah LTR' },
97
+ { value: 'sideways-rl', css: 'writing-mode: sideways-rl;', description: 'Sideways RTL', descriptionMs: 'Menyerong RTL' },
98
+ { value: 'sideways-lr', css: 'writing-mode: sideways-lr;', description: 'Sideways LTR', descriptionMs: 'Menyerong LTR' }
99
+ ],
100
+ examples: [
101
+ { code: '<div visual="writing-mode:vertical-rl">Vertical text</div>', description: 'Vertical text RTL' },
102
+ { code: '<div visual="writing-mode:horizontal-tb">Horizontal text</div>', description: 'Horizontal text LTR' }
103
+ ],
104
+ preview: [
105
+ {
106
+ title: 'Writing Mode',
107
+ titleMs: 'Mod Penulisan',
108
+ description: 'Control text direction and orientation',
109
+ descriptionMs: 'Kawal arah dan orientasi teks',
110
+ html: `<div layout="flex" space="g:medium p:medium" visual="bg:neutral-100 dark:bg:neutral-900 rounded:medium">
111
+ <div space="p:small" visual="bg:primary text:white rounded:small">horizontal-tb</div>
112
+ <div space="p:small" visual="bg:primary text:white rounded:small">vertical-rl</div>
113
+ </div>`,
114
+ highlightValue: 'writing-mode:vertical-rl'
115
+ }
116
+ ]
117
+ };
118
+
119
+ // Export all performance definitions
120
+ export const performanceDefinitions = {
121
+ contentVisibility,
122
+ contain,
123
+ writingMode
124
+ };
125
+
126
+ export default performanceDefinitions;
@@ -414,11 +414,12 @@ export const lineHeight = {
414
414
  export const borderRadius = {
415
415
  name: 'border-radius',
416
416
  property: 'visual',
417
- syntax: 'visual="rounded:[value]"',
418
- description: 'Set border radius',
419
- descriptionMs: 'Tetapkan jejari sempadan',
417
+ syntax: 'visual="rounded:[value]" | visual="rounded-{t|b|l|r|tl|tr|bl|br}:[value]"',
418
+ description: 'Set border radius for all corners or specific corners',
419
+ descriptionMs: 'Tetapkan jejari sempadan untuk semua bucu atau bucu tertentu',
420
420
  category: 'visual',
421
421
  usesScale: 'radius',
422
+ supportsArbitrary: true,
422
423
  values: [
423
424
  { value: 'none', css: 'border-radius: var(--r-none);', description: 'No rounding', descriptionMs: 'Tiada pembulatan' },
424
425
  { value: 'small', css: 'border-radius: var(--r-small);', description: 'Small radius', descriptionMs: 'Jejari kecil' },
@@ -427,8 +428,10 @@ export const borderRadius = {
427
428
  { value: 'round', css: 'border-radius: var(--r-round);', description: 'Fully round', descriptionMs: 'Sepenuhnya bulat' }
428
429
  ],
429
430
  examples: [
430
- { code: '<div visual="rounded:medium">Rounded corners</div>', description: 'Medium radius' },
431
- { code: '<div visual="rounded:round">Pill shape</div>', description: 'Pill' }
431
+ { code: '<div visual="rounded:medium">Rounded corners</div>', description: 'All corners rounded' },
432
+ { code: '<div visual="rounded:round">Pill shape</div>', description: 'Fully round' },
433
+ { code: '<div visual="rounded-t:medium">Top rounded</div>', description: 'Top corners only' },
434
+ { code: '<div visual="rounded-tl:big rounded-br:big">Opposite corners</div>', description: 'Specific corners' }
432
435
  ],
433
436
  footnotes: [
434
437
  {
@@ -446,12 +449,25 @@ export const borderRadius = {
446
449
  description: 'Round element corners from subtle to pill-shaped',
447
450
  descriptionMs: 'Bulatkan sudut elemen dari halus hingga berbentuk pil',
448
451
  html: `<div layout="flex" space="g:medium p:medium" visual="bg:neutral-100 dark:bg:neutral-900 rounded:medium">
449
- <div space="p:small" visual="bg:primary text:white rounded:none">none</div>
450
- <div space="p:small" visual="bg:primary text:white rounded:small">small</div>
451
- <div space="p:small" visual="bg:primary text:white rounded:medium">medium</div>
452
- <div space="p:small" visual="bg:primary text:white rounded:round">round</div>
452
+ <div space="p:small" visual="bg:primary text:white rounded:none">none</div>
453
+ <div space="p:small" visual="bg:primary text:white rounded:small">small</div>
454
+ <div space="p:small" visual="bg:primary text:white rounded:medium">medium</div>
455
+ <div space="p:small" visual="bg:primary text:white rounded:round">round</div>
453
456
  </div>`,
454
457
  highlightValue: 'rounded:medium'
458
+ },
459
+ {
460
+ title: 'Directional Border Radius',
461
+ titleMs: 'Jejari Sempadan Arah',
462
+ description: 'Round specific corners for unique shapes',
463
+ descriptionMs: 'Bulatkan bucu tertentu untuk bentuk unik',
464
+ html: `<div layout="flex" space="g:medium p:medium" visual="bg:neutral-100 dark:bg:neutral-900 rounded:medium">
465
+ <div space="p:small" visual="bg:primary text:white rounded-t:medium">top</div>
466
+ <div space="p:small" visual="bg:primary text:white rounded-b:medium">bottom</div>
467
+ <div space="p:small" visual="bg:primary text:white rounded-l:medium">left</div>
468
+ <div space="p:small" visual="bg:primary text:white rounded-r:medium">right</div>
469
+ </div>`,
470
+ highlightValue: 'rounded-t:medium'
455
471
  }
456
472
  ]
457
473
  };