@jjlmoya/utils-developer 1.16.0 → 1.18.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 (63) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +7 -1
  4. package/src/index.ts +4 -0
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/calculadoraTiempoDatos/data-time-calculator-web-speed-impact.css +498 -499
  8. package/src/tool/jwtDecoder/bibliography.astro +14 -0
  9. package/src/tool/jwtDecoder/bibliography.ts +12 -0
  10. package/src/tool/jwtDecoder/component.astro +209 -0
  11. package/src/tool/jwtDecoder/entry.ts +30 -0
  12. package/src/tool/jwtDecoder/i18n/de.ts +248 -0
  13. package/src/tool/jwtDecoder/i18n/en.ts +247 -0
  14. package/src/tool/jwtDecoder/i18n/es.ts +248 -0
  15. package/src/tool/jwtDecoder/i18n/fr.ts +248 -0
  16. package/src/tool/jwtDecoder/i18n/id.ts +248 -0
  17. package/src/tool/jwtDecoder/i18n/it.ts +248 -0
  18. package/src/tool/jwtDecoder/i18n/ja.ts +248 -0
  19. package/src/tool/jwtDecoder/i18n/ko.ts +248 -0
  20. package/src/tool/jwtDecoder/i18n/nl.ts +247 -0
  21. package/src/tool/jwtDecoder/i18n/pl.ts +247 -0
  22. package/src/tool/jwtDecoder/i18n/pt.ts +247 -0
  23. package/src/tool/jwtDecoder/i18n/ru.ts +248 -0
  24. package/src/tool/jwtDecoder/i18n/sv.ts +248 -0
  25. package/src/tool/jwtDecoder/i18n/tr.ts +248 -0
  26. package/src/tool/jwtDecoder/i18n/zh.ts +248 -0
  27. package/src/tool/jwtDecoder/index.ts +11 -0
  28. package/src/tool/jwtDecoder/jwt-decoder-parser-and-claims-inspector.css +358 -0
  29. package/src/tool/jwtDecoder/logic.ts +96 -0
  30. package/src/tool/jwtDecoder/seo.astro +15 -0
  31. package/src/tool/jwtDecoder/ui.ts +38 -0
  32. package/src/tool/serpPixelSimulator/bibliography.astro +11 -4
  33. package/src/tool/serpPixelSimulator/index.ts +0 -4
  34. package/src/tool/serpPixelSimulator/seo.astro +8 -5
  35. package/src/tool/visualCssGridFlexboxGenerator/bibliography.astro +14 -0
  36. package/src/tool/visualCssGridFlexboxGenerator/bibliography.ts +20 -0
  37. package/src/tool/visualCssGridFlexboxGenerator/canvas.ts +103 -0
  38. package/src/tool/visualCssGridFlexboxGenerator/component.astro +218 -0
  39. package/src/tool/visualCssGridFlexboxGenerator/entry.ts +30 -0
  40. package/src/tool/visualCssGridFlexboxGenerator/i18n/de.ts +110 -0
  41. package/src/tool/visualCssGridFlexboxGenerator/i18n/en.ts +256 -0
  42. package/src/tool/visualCssGridFlexboxGenerator/i18n/es.ts +110 -0
  43. package/src/tool/visualCssGridFlexboxGenerator/i18n/fr.ts +110 -0
  44. package/src/tool/visualCssGridFlexboxGenerator/i18n/id.ts +110 -0
  45. package/src/tool/visualCssGridFlexboxGenerator/i18n/it.ts +110 -0
  46. package/src/tool/visualCssGridFlexboxGenerator/i18n/ja.ts +110 -0
  47. package/src/tool/visualCssGridFlexboxGenerator/i18n/ko.ts +110 -0
  48. package/src/tool/visualCssGridFlexboxGenerator/i18n/nl.ts +110 -0
  49. package/src/tool/visualCssGridFlexboxGenerator/i18n/pl.ts +110 -0
  50. package/src/tool/visualCssGridFlexboxGenerator/i18n/pt.ts +110 -0
  51. package/src/tool/visualCssGridFlexboxGenerator/i18n/ru.ts +110 -0
  52. package/src/tool/visualCssGridFlexboxGenerator/i18n/sv.ts +110 -0
  53. package/src/tool/visualCssGridFlexboxGenerator/i18n/tr.ts +110 -0
  54. package/src/tool/visualCssGridFlexboxGenerator/i18n/zh.ts +110 -0
  55. package/src/tool/visualCssGridFlexboxGenerator/index.ts +11 -0
  56. package/src/tool/visualCssGridFlexboxGenerator/interactions.ts +45 -0
  57. package/src/tool/visualCssGridFlexboxGenerator/logic.ts +49 -0
  58. package/src/tool/visualCssGridFlexboxGenerator/presets.ts +63 -0
  59. package/src/tool/visualCssGridFlexboxGenerator/seo.astro +15 -0
  60. package/src/tool/visualCssGridFlexboxGenerator/ui.ts +58 -0
  61. package/src/tool/visualCssGridFlexboxGenerator/visual-css-grid-flexbox-generator.css +816 -0
  62. package/src/tool/visualCssGridFlexboxGenerator/workspace.ts +192 -0
  63. package/src/tools.ts +3 -1
@@ -0,0 +1,218 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+ import type { VisualCssGridFlexboxGeneratorUI } from './ui';
4
+ import './visual-css-grid-flexbox-generator.css';
5
+
6
+ interface Props {
7
+ ui?: Record<string, unknown>;
8
+ }
9
+
10
+ const { ui } = Astro.props;
11
+ const t = (ui ?? {}) as VisualCssGridFlexboxGeneratorUI;
12
+ ---
13
+
14
+ <div id="vcg-root" class="vcg-root" data-i18n={JSON.stringify(t)}>
15
+ <section class="vcg-card">
16
+ <div class="vcg-stage-panel">
17
+ <div class="vcg-toolbar">
18
+ <div class="vcg-mode" role="group" aria-label={t.modeLabel}>
19
+ <button id="vcg-flex" class="vcg-mode-button vcg-active" type="button" aria-pressed="true">
20
+ <Icon name="mdi:format-align-justify" />
21
+ {t.flexMode}
22
+ </button>
23
+ <button id="vcg-grid" class="vcg-mode-button" type="button" aria-pressed="false">
24
+ <Icon name="mdi:grid" />
25
+ {t.gridMode}
26
+ </button>
27
+ </div>
28
+ <div class="vcg-actions">
29
+ <button id="vcg-add" class="vcg-icon-button" type="button" aria-label={t.addItem} title={t.addItem}>
30
+ <Icon name="mdi:plus" />
31
+ </button>
32
+ <button id="vcg-remove" class="vcg-icon-button" type="button" aria-label={t['removeItem']} title={t['removeItem']}>
33
+ <Icon name="mdi:minus" />
34
+ </button>
35
+ <button id="vcg-reset" class="vcg-icon-button" type="button" aria-label={t.resetLayout} title={t.resetLayout}>
36
+ <Icon name="mdi:restore" />
37
+ </button>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="vcg-presets" id="vcg-presets" role="group" aria-label={t.presetsLabel}>
42
+ <button class="vcg-preset-button" data-preset="navbar" type="button">
43
+ <Icon name="mdi:menu" />{t.presetNavbar}
44
+ </button>
45
+ <button class="vcg-preset-button" data-preset="cards" type="button">
46
+ <Icon name="mdi:view-dashboard" />{t.presetCards}
47
+ </button>
48
+ <button class="vcg-preset-button" data-preset="hero" type="button">
49
+ <Icon name="mdi:image-area" />{t.presetHero}
50
+ </button>
51
+ <button class="vcg-preset-button" data-preset="sidebar" type="button">
52
+ <Icon name="mdi:page-layout-sidebar-left" />{t.presetSidebar}
53
+ </button>
54
+ <button class="vcg-preset-button" data-preset="gallery" type="button">
55
+ <Icon name="mdi:view-gallery" />{t.presetGallery}
56
+ </button>
57
+ </div>
58
+
59
+ <div class="vcg-canvas-shell" id="vcg-canvas-shell">
60
+ <div id="vcg-canvas" class="vcg-canvas" aria-label={t.canvasLabel}>
61
+ <div class="vcg-item" data-item="1"><span>{t.itemPrefix} 1</span></div>
62
+ <div class="vcg-item" data-item="2"><span>{t.itemPrefix} 2</span></div>
63
+ <div class="vcg-item" data-item="3"><span>{t.itemPrefix} 3</span></div>
64
+ <div class="vcg-item" data-item="4"><span>{t.itemPrefix} 4</span></div>
65
+ <div class="vcg-item" data-item="5"><span>{t.itemPrefix} 5</span></div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+
70
+ <div class="vcg-control-panel">
71
+ <div class="vcg-slider-grid">
72
+ <label class="vcg-control" for="vcg-gap">
73
+ <span>{t.gapLabel}</span>
74
+ <strong id="vcg-gap-value">24px</strong>
75
+ <input id="vcg-gap" type="range" min="0" max="80" value="24" />
76
+ </label>
77
+
78
+ <label class="vcg-control vcg-grid-only vcg-hidden" id="vcg-columns-wrap" for="vcg-columns">
79
+ <span>{t.columnsLabel}</span>
80
+ <strong id="vcg-columns-value">3</strong>
81
+ <input id="vcg-columns" type="range" min="1" max="8" value="3" />
82
+ </label>
83
+
84
+ <label class="vcg-control" for="vcg-width">
85
+ <span>{t.widthLabel}</span>
86
+ <strong id="vcg-width-value">760px</strong>
87
+ <input id="vcg-width" type="range" min="280" max="1200" value="760" />
88
+ </label>
89
+
90
+ <label class="vcg-control" for="vcg-height">
91
+ <span>{t.heightLabel}</span>
92
+ <strong id="vcg-height-value">380px</strong>
93
+ <input id="vcg-height" type="range" min="180" max="700" value="380" />
94
+ </label>
95
+
96
+ <label class="vcg-control" for="vcg-item-size">
97
+ <span>{t.itemSizeLabel}</span>
98
+ <strong id="vcg-item-size-value">104px</strong>
99
+ <input id="vcg-item-size" type="range" min="56" max="200" value="104" />
100
+ </label>
101
+ </div>
102
+
103
+ <div class="vcg-select-grid">
104
+ <label class="vcg-select-wrap vcg-flex-only" id="vcg-direction-wrap" for="vcg-direction">
105
+ <span>{t.directionLabel}</span>
106
+ <select id="vcg-direction" class="vcg-select">
107
+ <option value="row" selected>{t.directionRow}</option>
108
+ <option value="row-reverse">{t.directionRowReverse}</option>
109
+ <option value="column">{t.directionColumn}</option>
110
+ <option value="column-reverse">{t.directionColumnReverse}</option>
111
+ </select>
112
+ </label>
113
+
114
+ <label class="vcg-select-wrap vcg-flex-only" id="vcg-wrap-wrap" for="vcg-wrap">
115
+ <span>{t.wrapLabel}</span>
116
+ <select id="vcg-wrap" class="vcg-select">
117
+ <option value="nowrap">{t.wrapNoWrap}</option>
118
+ <option value="wrap" selected>{t.wrapWrap}</option>
119
+ <option value="wrap-reverse">{t.wrapWrapReverse}</option>
120
+ </select>
121
+ </label>
122
+
123
+ <label class="vcg-select-wrap" for="vcg-justify">
124
+ <span>{t.justifyLabel}</span>
125
+ <select id="vcg-justify" class="vcg-select">
126
+ <option value="start">{t.justifyStart}</option>
127
+ <option value="center" selected>{t.justifyCenter}</option>
128
+ <option value="end">{t.justifyEnd}</option>
129
+ <option value="space-between">{t.justifyBetween}</option>
130
+ <option value="space-around">{t.justifyAround}</option>
131
+ <option value="space-evenly">{t.justifyEvenly}</option>
132
+ </select>
133
+ </label>
134
+
135
+ <label class="vcg-select-wrap" for="vcg-align">
136
+ <span>{t.alignLabel}</span>
137
+ <select id="vcg-align" class="vcg-select">
138
+ <option value="start">{t.alignStart}</option>
139
+ <option value="center" selected>{t.alignCenter}</option>
140
+ <option value="end">{t.alignEnd}</option>
141
+ <option value="stretch">{t.alignStretch}</option>
142
+ <option value="baseline">{t.alignBaseline}</option>
143
+ </select>
144
+ </label>
145
+
146
+ <label class="vcg-select-wrap vcg-flex-only" id="vcg-align-content-wrap" for="vcg-align-content">
147
+ <span>{t.alignContentLabel}</span>
148
+ <select id="vcg-align-content" class="vcg-select">
149
+ <option value="start">{t.alignContentStart}</option>
150
+ <option value="center">{t.alignContentCenter}</option>
151
+ <option value="end">{t.alignContentEnd}</option>
152
+ <option value="space-between">{t.alignContentBetween}</option>
153
+ <option value="space-around">{t.alignContentAround}</option>
154
+ <option value="space-evenly">{t.alignContentEvenly}</option>
155
+ <option value="stretch" selected>{t.alignContentStretch}</option>
156
+ </select>
157
+ </label>
158
+ </div>
159
+
160
+ <div class="vcg-code-panel" id="vcg-code-panel">
161
+ <div class="vcg-code-header">
162
+ <h2>{t.codeTitle}</h2>
163
+ <button id="vcg-copy" class="vcg-copy-button" type="button" aria-label={t.copyCode} title={t.copyCode}>
164
+ <Icon name="mdi:content-copy" />
165
+ </button>
166
+ </div>
167
+ <pre id="vcg-code" class="vcg-code" aria-label={t.outputLabel}></pre>
168
+ </div>
169
+ </div>
170
+ </section>
171
+ </div>
172
+
173
+ <script>
174
+ import type { VisualCssGridFlexboxGeneratorUI } from './ui';
175
+ import { createWorkspace, setupSpanHandler, spawnConfetti } from './workspace';
176
+
177
+ const root = document.getElementById('vcg-root') as HTMLElement;
178
+ const t = JSON.parse(root.dataset.i18n ?? '{}') as VisualCssGridFlexboxGeneratorUI;
179
+ const ws = createWorkspace(root, t);
180
+
181
+ setupSpanHandler(ws.canvas, () => ws.readState().mode, t.itemPrefix, () => ws.update());
182
+
183
+ [
184
+ ws.gapInput, ws.columnsInput, ws.widthInput, ws.heightInput, ws.itemSizeInput,
185
+ ws.justifySelect, ws.alignSelect, ws.directionSelect, ws.wrapSelect, ws.alignContentSelect,
186
+ ].forEach((el) => {
187
+ el.addEventListener('input', () => ws.update());
188
+ el.addEventListener('change', () => ws.update());
189
+ });
190
+
191
+ document.getElementById('vcg-flex')?.addEventListener('click', () => ws.setMode('flex'));
192
+ document.getElementById('vcg-grid')?.addEventListener('click', () => ws.setMode('grid'));
193
+ document.getElementById('vcg-add')?.addEventListener('click', () => ws.addItem());
194
+ document.getElementById('vcg-remove')?.addEventListener('click', () => ws['removeItem']());
195
+ document.getElementById('vcg-reset')?.addEventListener('click', () => ws.resetLayout());
196
+
197
+ document.querySelectorAll('.vcg-preset-button').forEach((btn) => {
198
+ btn.addEventListener('click', () => {
199
+ const preset = (btn as HTMLElement).dataset.preset;
200
+ if (preset) ws.applyPreset(preset);
201
+ });
202
+ });
203
+
204
+ const copyBtn = document.getElementById('vcg-copy') as HTMLButtonElement | null;
205
+ copyBtn?.addEventListener('click', async () => {
206
+ const rect = copyBtn.getBoundingClientRect();
207
+ await navigator.clipboard.writeText(ws.code.textContent ?? '');
208
+ copyBtn.dataset.copied = t.copied;
209
+ copyBtn.classList.add('vcg-copied');
210
+ spawnConfetti(rect.left + rect.width / 2, rect.top);
211
+ window.setTimeout(() => {
212
+ delete copyBtn.dataset.copied;
213
+ copyBtn.classList.remove('vcg-copied');
214
+ }, 1400);
215
+ });
216
+
217
+ ws.update();
218
+ </script>
@@ -0,0 +1,30 @@
1
+ import type { DeveloperToolEntry, ToolLocaleContent } from '../../types';
2
+
3
+ import type { VisualCssGridFlexboxGeneratorUI } from './ui';
4
+
5
+ export type VisualCssGridFlexboxGeneratorLocaleContent = ToolLocaleContent<VisualCssGridFlexboxGeneratorUI>;
6
+
7
+ export const visualCssGridFlexboxGenerator: DeveloperToolEntry<VisualCssGridFlexboxGeneratorUI> = {
8
+ id: 'visual-css-grid-flexbox-generator',
9
+ icons: {
10
+ bg: 'mdi:view-grid-plus',
11
+ fg: 'mdi:drag-variant',
12
+ },
13
+ i18n: {
14
+ de: async () => (await import('./i18n/de')).content,
15
+ en: async () => (await import('./i18n/en')).content,
16
+ es: async () => (await import('./i18n/es')).content,
17
+ fr: async () => (await import('./i18n/fr')).content,
18
+ id: async () => (await import('./i18n/id')).content,
19
+ it: async () => (await import('./i18n/it')).content,
20
+ ja: async () => (await import('./i18n/ja')).content,
21
+ ko: async () => (await import('./i18n/ko')).content,
22
+ nl: async () => (await import('./i18n/nl')).content,
23
+ pl: async () => (await import('./i18n/pl')).content,
24
+ pt: async () => (await import('./i18n/pt')).content,
25
+ ru: async () => (await import('./i18n/ru')).content,
26
+ sv: async () => (await import('./i18n/sv')).content,
27
+ tr: async () => (await import('./i18n/tr')).content,
28
+ zh: async () => (await import('./i18n/zh')).content,
29
+ },
30
+ };
@@ -0,0 +1,110 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { VisualCssGridFlexboxGeneratorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
5
+
6
+ const slug = 'visueller-css-grid-flexbox-generator';
7
+ const title = 'Visueller CSS Grid & Flexbox Layout Generator';
8
+ const description = 'Gestalte responsive Layouts durch Verschieben visueller Blöcke, skalieren des Containers, anpassen der Ausrichtung und Verwenden von Presets - dann sauberes natives CSS sofort kopieren.';
9
+
10
+ const howTo = [
11
+ { name: 'Preset oder Flexbox / Grid wählen', text: 'Beginne mit einem Layout-Preset (Navbar, Cards, Hero, Sidebar, Gallery) oder wechsle manuell zwischen Flexbox und Grid.' },
12
+ { name: 'Layout skalieren', text: 'Ziehe den Container an der unteren Ecke, um zu testen, wie das Design auf verfügbaren Platz reagiert.' },
13
+ { name: 'Ausrichtung anpassen', text: 'Nutze Schieberegler und Dropdowns für Richtung, Wrap, Abstand, Spalten, justify-content, align-items, align-content, Breite, Höhe und Item-Größe.' },
14
+ { name: 'Produktionsreifes CSS kopieren', text: 'Kopiere das generierte CSS, wenn das visuelle Ergebnis der gewünschten Struktur entspricht. Keine Framework-Abhängigkeiten.' },
15
+ ];
16
+
17
+ const faq = [
18
+ { question: 'Wann sollte ich Flexbox statt CSS Grid verwenden?', answer: 'Nutze Flexbox bei eindimensionalen Layouts - Navigationsleisten, Button-Gruppen, umfließende Cards, zentrierte Inhalte. Grid eignet sich, wenn Zeilen und Spalten gemeinsam wichtig sind - Dashboards, Galerien, Formulare, strukturierte Seitenbereiche.' },
19
+ { question: 'Kann dieser Generator responsive Layouts erstellen?', answer: 'Ja. Das generierte CSS verwendet natives Flex-Wrapping oder Grid-Repeat-Spalten. Skaliere die visuelle Leinwand, um Abstände und Ausrichtung bei verschiedenen Größen zu testen.' },
20
+ { question: 'Warum fühlen sich justify-content und align-items in Grid und Flex unterschiedlich an?', answer: 'Flexbox verteilt Elemente entlang einer Haupt- und Querachse. Grid platziert Elemente zuerst in Tracks und richtet dann den Inhalt darin aus. Der Wechsel zwischen beiden Modi macht diesen Unterschied sofort sichtbar.' },
21
+ { question: 'Ist das generierte CSS an ein Framework gebunden?', answer: 'Nein. Das Ergebnis ist reines natives CSS. Verwende es in einfachem HTML, Astro, React, Vue, Svelte, Web Components oder jedem Projekt mit Standard-CSS.' },
22
+ { question: 'Wozu dienen die Layout-Presets?', answer: 'Presets beschleunigen gängige Layouts, damit du realistische Konfigurationen siehst, ohne bei Null anzufangen. Jedes Preset setzt Modus, Richtung, Wrap, Ausrichtung und Containergröße für ein praxisnahes Muster.' },
23
+ ];
24
+
25
+ const ui: VisualCssGridFlexboxGeneratorUI = {
26
+ modeLabel: 'Layout-Modus',
27
+ flexMode: 'Flexbox',
28
+ gridMode: 'Grid',
29
+ canvasLabel: 'Interaktive Layout-Leinwand',
30
+ addItem: 'Element hinzufügen',
31
+ removeItem: 'Element entfernen',
32
+ resetLayout: 'Layout zurücksetzen',
33
+ gapLabel: 'Abstand',
34
+ columnsLabel: 'Grid-Spalten',
35
+ widthLabel: 'Container-Breite',
36
+ heightLabel: 'Container-Höhe',
37
+ justifyLabel: 'Ausrichten',
38
+ alignLabel: 'Ausrichten',
39
+ itemSizeLabel: 'Elementgröße',
40
+ codeTitle: 'Generiertes CSS',
41
+ copyCode: 'CSS kopieren',
42
+ copied: 'Kopiert!',
43
+ dragHint: 'Canvas an der Ecke skalieren - CSS aktualisiert sich live!',
44
+ outputLabel: 'Generierte CSS-Ausgabe',
45
+ justifyStart: 'Start',
46
+ justifyCenter: 'Mitte',
47
+ justifyEnd: 'Ende',
48
+ justifyBetween: 'Space between',
49
+ justifyAround: 'Space around',
50
+ justifyEvenly: 'Space evenly',
51
+ alignStart: 'Start',
52
+ alignCenter: 'Mitte',
53
+ alignEnd: 'Ende',
54
+ alignStretch: 'Strecken',
55
+ alignBaseline: 'Basislinie',
56
+ itemPrefix: 'Block',
57
+ directionLabel: 'Richtung',
58
+ directionRow: 'Reihe →',
59
+ directionRowReverse: '← Reihe rev',
60
+ directionColumn: 'Spalte ↓',
61
+ directionColumnReverse: '↑ Spalte rev',
62
+ wrapLabel: 'Umbruch',
63
+ wrapNoWrap: 'Kein Umbruch',
64
+ wrapWrap: 'Umbruch',
65
+ wrapWrapReverse: 'Umbruch rev',
66
+ alignContentLabel: 'Inhalt ausr.',
67
+ alignContentStart: 'Start',
68
+ alignContentCenter: 'Mitte',
69
+ alignContentEnd: 'Ende',
70
+ alignContentBetween: 'Space between',
71
+ alignContentAround: 'Space around',
72
+ alignContentEvenly: 'Space evenly',
73
+ alignContentStretch: 'Strecken',
74
+ presetsLabel: 'Presets',
75
+ presetNavbar: 'Navbar',
76
+ presetCards: 'Cards',
77
+ presetHero: 'Hero',
78
+ presetSidebar: 'Sidebar',
79
+ presetGallery: 'Galerie',
80
+ shakeLimit: 'Mindestens 2 Elemente nötig!',
81
+ spanHint: 'Doppelklick auf ein Element ändert die Spaltenspanne (1-3) im Grid-Modus',
82
+ };
83
+
84
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
85
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howTo.map((step, index) => ({ '@type': 'HowToStep', position: index + 1, name: step.name, text: step.text })) };
86
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'DeveloperApplication', operatingSystem: 'Any', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' } };
87
+
88
+ export const content: ToolLocaleContent<VisualCssGridFlexboxGeneratorUI> = {
89
+ slug, title, description, ui,
90
+ faqTitle: 'CSS-Layout-Generator FAQ',
91
+ faq,
92
+ bibliographyTitle: 'CSS Grid und Flexbox Referenzen',
93
+ bibliography,
94
+ howTo,
95
+ schemas: [appSchema, faqSchema, howToSchema],
96
+ seo: [
97
+ { type: 'title', text: 'Erstelle CSS-Layouts durch Beobachten des Verhaltens, nicht durch Auswendiglernen', level: 2 },
98
+ { type: 'paragraph', html: 'CSS Grid und Flexbox sind mächtig, weil sie Layout-Beziehungen statt fester Koordinaten beschreiben. Schwierig ist vorherzusagen, wie <strong>gap</strong>, <strong>justify-content</strong>, <strong>align-items</strong>, Richtung, Umbruch, Tracks und verfügbarer Platz interagieren. Dieser Generator verwandelt abstrakte Eigenschaften in einen Live-Spielplatz mit Presets und Echtzeit-CSS.' },
99
+ { type: 'stats', columns: 3, items: [{ value: '5', label: 'Schnellstart-Layout-Presets', icon: 'mdi:view-grid-plus' }, { value: 'Live', label: 'CSS aktualisiert bei jeder Änderung', icon: 'mdi:code-braces' }, { value: '0', label: 'Framework-Abhängigkeiten im CSS', icon: 'mdi:language-css3' }] },
100
+ { type: 'title', text: 'Flexbox vs Grid: Modell wählen vor Ausrichtung optimieren', level: 3 },
101
+ { type: 'comparative', columns: 2, items: [{ title: 'Flexbox', description: 'Am besten für eindimensionale Flüsse, wo Elemente in einer Reihe oder Spalte ausgerichtet sind und natürlich umbrechen.', icon: 'mdi:format-align-justify', highlight: true, points: ['Navigation', 'Button-Gruppen', 'Umfließende Cards', 'Zentrierter Inhalt'] }, { title: 'CSS Grid', description: 'Am besten für zweidimensionale Strukturen, wo Reihen und Spalten die Form der Komposition definieren.', icon: 'mdi:grid', points: ['Dashboards', 'Galerien', 'Formulare', 'Redaktionelle Bereiche'] }] },
102
+ { type: 'title', text: 'Was jede Steuerung lehrt', level: 3 },
103
+ { type: 'table', headers: ['Steuerung', 'CSS-Eigenschaft', 'Worauf achten'], rows: [['Richtung', '<code>flex-direction</code>', 'Wie die Hauptachse fließt - von Reihe zu Spalte wechseln ändert die gesamte Layout-Logik.'], ['Umbruch', '<code>flex-wrap</code>', 'Ob Elemente in einer Zeile bleiben oder bei Platzmangel umbrechen.'], ['Abstand', '<code>gap</code>', 'Der Raum zwischen Elementen ohne Ränder, die an Kanten brechen.'], ['Ausrichten', '<code>justify-content</code>', 'Wie freier Raum entlang der Hauptachse verteilt wird.'], ['Ausrichten', '<code>align-items</code>', 'Wie Elemente auf der Querachse sitzen.'], ['Inhalt ausr.', '<code>align-content</code>', 'Wie umgebrochene Flex-Zeilen oder Grid-Zeilen zusätzlichen Querachsen-Raum verteilen.'], ['Spalten', '<code>grid-template-columns</code>', 'Wie viele gleiche Tracks das Grid erstellt, bevor Elemente in die nächste Zeile umbrechen.'], ['Container-Größe', '<code>width</code> und <code>min-height</code>', 'Wie dasselbe CSS bei verändertem Platz reagiert.']] },
104
+ { type: 'tip', title: 'Zuerst skalieren, dann optimieren', html: 'Skaliere die Leinwand zuerst auf eine realistische Größe. Dann Abstand und Ausrichtung anpassen. So funktioniert das generierte CSS unter echten Bedingungen.' },
105
+ { type: 'title', text: 'Sauberes CSS zum Anpassen', level: 3 },
106
+ { type: 'code', ariaLabel: 'Beispiel generiertes CSS', code: '.layout-playground {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n gap: 24px;\n justify-content: center;\n align-items: center;\n}' },
107
+ { type: 'diagnostic', variant: 'info', title: 'Warum visuelles Skalieren wichtig ist', html: 'Viele Layout-Bugs erscheinen nur, wenn der Container schmaler, höher oder mit anderer Elementanzahl gefüllt ist. Das Skalieren während der Live-CSS-Aktualisierung hilft, ungünstige Umbrüche und ausrichtungsbedingte Probleme zu erkennen.' },
108
+ { type: 'summary', title: 'Empfohlener Workflow', items: ['Preset wählen oder Flexbox für eindimensionale und Grid für zweidimensionale Strukturen.', 'Leinwand vor dem Kopieren skalieren, damit das Layout realistischen Einschränkungen standhält.', 'Abstand für Zwischenräume nutzen statt Ränder an jedem Kind.' , 'Generiertes CSS als Ausgangspunkt kopieren, dann projektspezifische Selektoren und Media Queries hinzufügen.'] },
109
+ ],
110
+ };
@@ -0,0 +1,256 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { VisualCssGridFlexboxGeneratorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
5
+
6
+ const slug = 'visual-css-grid-flexbox-generator';
7
+ const title = 'Visual CSS Grid & Flexbox Layout Generator';
8
+ const description = 'Design responsive layouts by dragging visual blocks, resizing the container, tuning alignment controls and using presets - then copy clean native CSS instantly.';
9
+
10
+ const howTo = [
11
+ {
12
+ name: 'Choose a preset or Flexbox / Grid',
13
+ text: 'Start with a layout preset (navbar, cards, hero, sidebar, gallery) or switch between Flexbox and Grid mode manually.',
14
+ },
15
+ {
16
+ name: 'Drag and resize the layout',
17
+ text: 'Reorder items by dragging them and resize the container from the corner handle to test how the design reacts to available space.',
18
+ },
19
+ {
20
+ name: 'Tune alignment controls',
21
+ text: 'Use sliders and selects for direction, wrap, gap, columns, justify-content, align-items, align-content, width, height and item size.',
22
+ },
23
+ {
24
+ name: 'Copy production-ready CSS',
25
+ text: 'Copy the generated CSS when the visual result matches the structure you want to implement. No framework dependencies.',
26
+ },
27
+ ];
28
+
29
+ const faq = [
30
+ {
31
+ question: 'When should I use Flexbox instead of CSS Grid?',
32
+ answer: 'Use Flexbox when the layout is mainly one-dimensional - navigation bars, button groups, wrapped cards, centered content. Use Grid when rows and columns matter together - dashboards, galleries, form layouts and structured page sections.',
33
+ },
34
+ {
35
+ question: 'Can this generator create responsive layouts?',
36
+ answer: 'Yes. The generated CSS uses native flex wrapping or grid repeat columns. Resize the visual canvas to test how spacing and alignment behave at different sizes before writing media queries.',
37
+ },
38
+ {
39
+ question: 'Why do justify-content and align-items feel different in grid and flex?',
40
+ answer: 'Flexbox distributes items along a main axis and cross axis. Grid places items into tracks first, then aligns content inside those tracks. Toggling both modes on the same canvas makes that behavioral difference immediately visible.',
41
+ },
42
+ {
43
+ question: 'Is the generated CSS tied to a framework?',
44
+ answer: 'No. The output is pure native CSS. Use it in plain HTML, Astro, React, Vue, Svelte, Web Components or any project that accepts standard CSS.',
45
+ },
46
+ {
47
+ question: 'What are the layout presets for?',
48
+ answer: 'Presets jump-start common layouts so you can see realistic configurations instead of starting from scratch. Each preset sets mode, direction, wrap, alignment and container size to match a real-world pattern.',
49
+ },
50
+ ];
51
+
52
+ const ui: VisualCssGridFlexboxGeneratorUI = {
53
+ modeLabel: 'Layout mode',
54
+ flexMode: 'Flexbox',
55
+ gridMode: 'Grid',
56
+ canvasLabel: 'Interactive layout canvas',
57
+ addItem: 'Add item',
58
+ removeItem: 'Remove item',
59
+ resetLayout: 'Reset layout',
60
+ gapLabel: 'Gap',
61
+ columnsLabel: 'Grid columns',
62
+ widthLabel: 'Container width',
63
+ heightLabel: 'Container height',
64
+ justifyLabel: 'Justify',
65
+ alignLabel: 'Align',
66
+ itemSizeLabel: 'Item size',
67
+ codeTitle: 'Generated CSS',
68
+ copyCode: 'Copy CSS',
69
+ copied: 'Copied!',
70
+ dragHint: 'Resize the bordered canvas from the corner handle - CSS updates live!',
71
+ outputLabel: 'Generated CSS output',
72
+ justifyStart: 'Start',
73
+ justifyCenter: 'Center',
74
+ justifyEnd: 'End',
75
+ justifyBetween: 'Space between',
76
+ justifyAround: 'Space around',
77
+ justifyEvenly: 'Space evenly',
78
+ alignStart: 'Start',
79
+ alignCenter: 'Center',
80
+ alignEnd: 'End',
81
+ alignStretch: 'Stretch',
82
+ alignBaseline: 'Baseline',
83
+ itemPrefix: 'Block',
84
+ directionLabel: 'Direction',
85
+ directionRow: 'Row →',
86
+ directionRowReverse: '← Row rev',
87
+ directionColumn: 'Column ↓',
88
+ directionColumnReverse: '↑ Col rev',
89
+ wrapLabel: 'Wrap',
90
+ wrapNoWrap: 'No wrap',
91
+ wrapWrap: 'Wrap',
92
+ wrapWrapReverse: 'Wrap rev',
93
+ alignContentLabel: 'Align content',
94
+ alignContentStart: 'Start',
95
+ alignContentCenter: 'Center',
96
+ alignContentEnd: 'End',
97
+ alignContentBetween: 'Space between',
98
+ alignContentAround: 'Space around',
99
+ alignContentEvenly: 'Space evenly',
100
+ alignContentStretch: 'Stretch',
101
+ presetsLabel: 'Presets',
102
+ presetNavbar: 'Navbar',
103
+ presetCards: 'Cards',
104
+ presetHero: 'Hero',
105
+ presetSidebar: 'Sidebar',
106
+ presetGallery: 'Gallery',
107
+ shakeLimit: 'Need at least 2 items!',
108
+ spanHint: 'Double-click an item to change its column span (1-3) in Grid mode',
109
+ };
110
+
111
+ const faqSchema: WithContext<FAQPage> = {
112
+ '@context': 'https://schema.org',
113
+ '@type': 'FAQPage',
114
+ mainEntity: faq.map((item) => ({
115
+ '@type': 'Question',
116
+ name: item.question,
117
+ acceptedAnswer: {
118
+ '@type': 'Answer',
119
+ text: item.answer,
120
+ },
121
+ })),
122
+ };
123
+
124
+ const howToSchema: WithContext<HowTo> = {
125
+ '@context': 'https://schema.org',
126
+ '@type': 'HowTo',
127
+ name: title,
128
+ description,
129
+ step: howTo.map((step, index) => ({
130
+ '@type': 'HowToStep',
131
+ position: index + 1,
132
+ name: step.name,
133
+ text: step.text,
134
+ })),
135
+ };
136
+
137
+ const appSchema: WithContext<SoftwareApplication> = {
138
+ '@context': 'https://schema.org',
139
+ '@type': 'SoftwareApplication',
140
+ name: title,
141
+ description,
142
+ applicationCategory: 'DeveloperApplication',
143
+ operatingSystem: 'Any',
144
+ offers: {
145
+ '@type': 'Offer',
146
+ price: '0',
147
+ priceCurrency: 'EUR',
148
+ },
149
+ };
150
+
151
+ export const content: ToolLocaleContent<VisualCssGridFlexboxGeneratorUI> = {
152
+ slug,
153
+ title,
154
+ description,
155
+ ui,
156
+ faqTitle: 'CSS layout generator FAQ',
157
+ faq,
158
+ bibliographyTitle: 'CSS Grid and Flexbox references',
159
+ bibliography,
160
+ howTo,
161
+ schemas: [appSchema, faqSchema, howToSchema],
162
+ seo: [
163
+ {
164
+ type: 'title',
165
+ text: 'Build CSS layouts by seeing the behavior, not memorizing properties',
166
+ level: 2,
167
+ },
168
+ {
169
+ type: 'paragraph',
170
+ html: 'CSS Grid and Flexbox are powerful because they describe layout relationships instead of fixed coordinates. The hard part is predicting how <strong>gap</strong>, <strong>justify-content</strong>, <strong>align-items</strong>, direction, wrapping, tracks and available space interact. This visual generator turns those abstract properties into a live playground with presets, drag-and-drop reordering and real-time CSS output.',
171
+ },
172
+ {
173
+ type: 'stats',
174
+ columns: 3,
175
+ items: [
176
+ { value: '5', label: 'Quick-start layout presets', icon: 'mdi:view-grid-plus' },
177
+ { value: 'Live', label: 'CSS output updates on every change', icon: 'mdi:code-braces' },
178
+ { value: '0', label: 'Framework dependencies in generated CSS', icon: 'mdi:language-css3' },
179
+ ],
180
+ },
181
+ {
182
+ type: 'title',
183
+ text: 'Flexbox vs Grid: choose the model before polishing alignment',
184
+ level: 3,
185
+ },
186
+ {
187
+ type: 'comparative',
188
+ columns: 2,
189
+ items: [
190
+ {
191
+ title: 'Flexbox',
192
+ description: 'Best for one-dimensional flows where items line up along a row or column and may wrap naturally.',
193
+ icon: 'mdi:format-align-justify',
194
+ highlight: true,
195
+ points: ['Navigation bars', 'Button groups', 'Wrapped cards', 'Centered content'],
196
+ },
197
+ {
198
+ title: 'CSS Grid',
199
+ description: 'Best for two-dimensional structures where rows and columns define the shape of the composition.',
200
+ icon: 'mdi:grid',
201
+ points: ['Dashboards', 'Galleries', 'Form layouts', 'Editorial sections'],
202
+ },
203
+ ],
204
+ },
205
+ {
206
+ type: 'title',
207
+ text: 'What each control teaches you',
208
+ level: 3,
209
+ },
210
+ {
211
+ type: 'table',
212
+ headers: ['Control', 'CSS property', 'What to watch'],
213
+ rows: [
214
+ ['Direction', '<code>flex-direction</code>', 'How the main axis flows - swap row for column to change the entire layout logic.'],
215
+ ['Wrap', '<code>flex-wrap</code>', 'Whether items stay on one line or flow to new rows when space runs out.'],
216
+ ['Gap', '<code>gap</code>', 'The space between items without adding margins that break at edges.'],
217
+ ['Justify', '<code>justify-content</code>', 'How free space is distributed along the main axis or inline axis.'],
218
+ ['Align', '<code>align-items</code>', 'How items sit on the cross axis or inside their grid area.'],
219
+ ['Align content', '<code>align-content</code>', 'How wrapped flex lines or grid rows distribute extra cross-axis space.'],
220
+ ['Grid columns', '<code>grid-template-columns</code>', 'How many equal tracks the grid creates before items wrap to another row.'],
221
+ ['Container size', '<code>width</code> and <code>min-height</code>', 'How the same CSS reacts when available space changes.'],
222
+ ],
223
+ },
224
+ {
225
+ type: 'tip',
226
+ title: 'Drag first, optimize second',
227
+ html: 'Start by dragging blocks into the visual order that matches the intended reading flow. Then adjust gap and alignment. This keeps the generated CSS aligned with document order instead of hiding structure with fragile positioning tricks.',
228
+ },
229
+ {
230
+ type: 'title',
231
+ text: 'Clean CSS output you can adapt',
232
+ level: 3,
233
+ },
234
+ {
235
+ type: 'code',
236
+ ariaLabel: 'Example generated CSS',
237
+ code: '.layout-playground {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n gap: 24px;\n justify-content: center;\n align-items: center;\n}',
238
+ },
239
+ {
240
+ type: 'diagnostic',
241
+ variant: 'info',
242
+ title: 'Why visual resizing matters',
243
+ html: 'Many layout bugs appear only when the container is narrower, taller or filled with different item counts. Resizing the playground while the CSS updates live helps you spot awkward wrapping, excessive gaps and alignment choices that only work at one viewport size.',
244
+ },
245
+ {
246
+ type: 'summary',
247
+ title: 'Best practice workflow',
248
+ items: [
249
+ 'Choose a preset or Flexbox for one-dimensional flows and Grid for two-dimensional structure.',
250
+ 'Resize the visual canvas before copying CSS so the layout survives realistic constraints.',
251
+ 'Use gap for spacing between items instead of adding margins to every child.',
252
+ 'Copy the generated CSS as a starting point, then add project-specific selectors and media queries.',
253
+ ],
254
+ },
255
+ ],
256
+ };