create-flowmo 1.2.1 → 1.2.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.
- package/index.js +4 -15
- package/package.json +1 -1
- package/skills/outsystems-ui/SKILL.md +110 -12
- package/skills/outsystems-ui/assets/layout-base.html +6 -5
- package/skills/outsystems-ui/assets/layout-blank.html +2 -1
- package/skills/outsystems-ui/assets/layout-side.html +2 -1
- package/skills/outsystems-ui/assets/layout-top.html +2 -1
- package/skills/outsystems-ui/references/screen-templates.md +20 -20
- package/skills/outsystems-ui/references/ui-patterns.md +37 -9
- package/template/index.html +172 -0
- package/template/screens/home.visual.html +100 -7
- package/template/scripts/device-detect.js +40 -0
package/index.js
CHANGED
|
@@ -88,6 +88,7 @@ async function init() {
|
|
|
88
88
|
// Copy starter template files (CSS, starter screen, data, logic)
|
|
89
89
|
const templateDir = path.join(__dirname, 'template');
|
|
90
90
|
await fs.copy(path.join(templateDir, 'theme'), path.join(projectPath, 'theme'));
|
|
91
|
+
await fs.copy(path.join(templateDir, 'scripts'), path.join(projectPath, 'scripts'));
|
|
91
92
|
await fs.copy(path.join(templateDir, 'screens'), path.join(projectPath, 'screens'));
|
|
92
93
|
await fs.copy(path.join(templateDir, 'data'), path.join(projectPath, 'data'));
|
|
93
94
|
await fs.copy(path.join(templateDir, 'logic'), path.join(projectPath, 'logic'));
|
|
@@ -138,22 +139,10 @@ export default defineConfig({
|
|
|
138
139
|
`;
|
|
139
140
|
await fs.writeFile(path.join(projectPath, 'vite.config.js'), viteConfig);
|
|
140
141
|
|
|
141
|
-
//
|
|
142
|
+
// Copy root index.html and inject project name
|
|
142
143
|
const safeName = projectName.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<head>
|
|
146
|
-
<meta charset="UTF-8" />
|
|
147
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
148
|
-
<title>${safeName}</title>
|
|
149
|
-
</head>
|
|
150
|
-
<body>
|
|
151
|
-
<h1>${safeName}</h1>
|
|
152
|
-
<p>Open any screen from the <code>screens/</code> folder:</p>
|
|
153
|
-
<ul id="screen-list"></ul>
|
|
154
|
-
</body>
|
|
155
|
-
</html>
|
|
156
|
-
`;
|
|
144
|
+
let indexHtml = await fs.readFile(path.join(templateDir, 'index.html'), 'utf-8');
|
|
145
|
+
indexHtml = indexHtml.replaceAll('{{PROJECT_NAME}}', safeName);
|
|
157
146
|
await fs.writeFile(path.join(projectPath, 'index.html'), indexHtml);
|
|
158
147
|
|
|
159
148
|
s.stop('Project scaffolded successfully!');
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ Build pixel-perfect OutSystems-compatible `.visual.html` prototypes using only t
|
|
|
20
20
|
## General Rules
|
|
21
21
|
|
|
22
22
|
1. **Utility-first styling** — use OSUI utility classes (`.padding-m`, `.text-bold`, `.background-primary`). Never use raw CSS properties or inline `style=""` attributes.
|
|
23
|
-
2. **12-column grid** — use `.columns2` through `.columns6` for responsive layouts.
|
|
23
|
+
2. **12-column grid** — use `.columns.columns2` through `.columns.columns6` for responsive layouts. The parent div MUST have BOTH the `.columns` base class AND the number class (e.g. `class="columns columns3 gutter-base"`). Children MUST use `.columns-item` — NOT `.column`. Without `.columns`, `display: flex` is missing and items stack vertically.
|
|
24
24
|
3. **No external JavaScript** — all interaction patterns are CSS/class-driven.
|
|
25
25
|
4. **Layout is CRITICAL** — every screen MUST use one of the three OutSystems layouts. Pick the right one and use the exact HTML structure from the layout template files. The `.active-screen` wrapper is REQUIRED or utility classes will not resolve. See the **Layouts** section below.
|
|
26
26
|
5. **Scroll model** — OSUI sets `html { overflow: hidden }`. The `.active-screen` div is the actual scroll container. The `grid.css` file provides `.active-screen { overflow-y: auto; height: 100vh; }` — make sure `grid.css` is linked.
|
|
@@ -360,19 +360,108 @@ Some patterns expose CSS custom properties for fine-tuning. Set these on the pat
|
|
|
360
360
|
| Rating | `--rating-size: 16px` |
|
|
361
361
|
| Scrollable Area | `--scrollable-area-width`, `--scrollable-area-height` |
|
|
362
362
|
|
|
363
|
+
## Responsive & Device Classes
|
|
364
|
+
|
|
365
|
+
OutSystems uses a **JavaScript-based** responsive system, not CSS media queries. A runtime script detects the viewport size and applies device + orientation classes to `<body>`. All OSUI responsive CSS rules are scoped to these body classes.
|
|
366
|
+
|
|
367
|
+
### Body Classes
|
|
368
|
+
|
|
369
|
+
| Viewport width | Body class | Orientation class |
|
|
370
|
+
|----------------|-----------|-------------------|
|
|
371
|
+
| `< 768px` | `phone` | `portrait` (w ≤ h) or `landscape` (w > h) |
|
|
372
|
+
| `768px – 1024px` | `tablet` | `portrait` or `landscape` |
|
|
373
|
+
| `> 1024px` | `desktop` | `landscape` (typically) |
|
|
374
|
+
|
|
375
|
+
In the real OutSystems platform, this is handled by the platform runtime. For static `.visual.html` files, include the **`device-detect.js`** script at the bottom of `<body>` — it replicates the same behavior:
|
|
376
|
+
|
|
377
|
+
```html
|
|
378
|
+
<script src="../scripts/device-detect.js"></script>
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
This script runs on load AND on resize, so the body class updates when the browser window changes size.
|
|
382
|
+
|
|
383
|
+
### Column Break Behavior
|
|
384
|
+
|
|
385
|
+
Columns patterns (`columns2` through `columns6`, `columns-small-left`, etc.) stay side-by-side on desktop. On tablet and phone, you control how they collapse using **break classes**:
|
|
386
|
+
|
|
387
|
+
| Break class | Effect on the target breakpoint |
|
|
388
|
+
|-------------|--------------------------------|
|
|
389
|
+
| *(none)* | Columns keep their desktop proportions — no stacking |
|
|
390
|
+
| `{device}-break-all` | **All** columns stack to 100% width (full-width rows) |
|
|
391
|
+
| `{device}-break-first` | **First** column breaks to 100% width; remaining stay side-by-side |
|
|
392
|
+
| `{device}-break-last` | **Last** column breaks to 100% width; preceding stay side-by-side |
|
|
393
|
+
| `{device}-break-middle` | Middle columns break — behavior varies by column count (see below) |
|
|
394
|
+
|
|
395
|
+
Where `{device}` is `tablet` or `phone`. You can combine both independently:
|
|
396
|
+
|
|
397
|
+
```html
|
|
398
|
+
<!-- On tablet: first column breaks. On phone: all columns stack. -->
|
|
399
|
+
<div class="columns columns3 gutter-base tablet-break-first phone-break-all">
|
|
400
|
+
<div class="columns-item">Sidebar</div>
|
|
401
|
+
<div class="columns-item">Main</div>
|
|
402
|
+
<div class="columns-item">Aside</div>
|
|
403
|
+
</div>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
#### `break-middle` Details
|
|
407
|
+
|
|
408
|
+
`break-middle` is a halfway collapse — it reduces the column count without going to full-width:
|
|
409
|
+
|
|
410
|
+
| Column type | `break-middle` result |
|
|
411
|
+
|-------------|----------------------|
|
|
412
|
+
| `columns2` | All items → 100% width (same as break-all) |
|
|
413
|
+
| `columns3` | Last item → 100% width; first two stay side-by-side |
|
|
414
|
+
| `columns4` | All items → 50% width (2×2 grid) |
|
|
415
|
+
| `columns5` / `columns6` | First 3 items → 33.333% width (3-col row); rest flow below |
|
|
416
|
+
| `columns-small-left`, `-medium-left`, `-small-right`, `-medium-right` | All items → 100% width |
|
|
417
|
+
|
|
418
|
+
### Gutter Classes
|
|
419
|
+
|
|
420
|
+
The gutter controls spacing between columns. It works with break classes — when columns stack, the gutter becomes vertical margin:
|
|
421
|
+
|
|
422
|
+
| Gutter | Class |
|
|
423
|
+
|--------|-------|
|
|
424
|
+
| None | `gutter-none` |
|
|
425
|
+
| XS (4px) | `gutter-xs` |
|
|
426
|
+
| S (8px) | `gutter-s` |
|
|
427
|
+
| Base (16px) | `gutter-base` |
|
|
428
|
+
| M (24px) | `gutter-m` |
|
|
429
|
+
| L (32px) | `gutter-l` |
|
|
430
|
+
| XL (40px) | `gutter-xl` |
|
|
431
|
+
| XXL (48px) | `gutter-xxl` |
|
|
432
|
+
|
|
433
|
+
### Display On Device
|
|
434
|
+
|
|
435
|
+
Show/hide entire elements per breakpoint:
|
|
436
|
+
|
|
437
|
+
```html
|
|
438
|
+
<div class="display-on-device-desktop">Desktop only content</div>
|
|
439
|
+
<div class="display-on-device-tablet">Tablet only content</div>
|
|
440
|
+
<div class="display-on-device-phone">Phone only content</div>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
These require the correct body class to work — so `device-detect.js` is required for static previews.
|
|
444
|
+
|
|
363
445
|
## Theme Customization
|
|
364
446
|
|
|
365
|
-
Every project ships three CSS files, loaded in this order:
|
|
447
|
+
Every project ships three CSS files and one JS file, loaded in this order:
|
|
366
448
|
|
|
367
449
|
1. **`outsystems-ui.css`** — the OSUI framework (patterns, utilities, layout). Never edit this.
|
|
368
450
|
2. **`grid.css`** — platform grid definitions (`.ThemeGrid_Container` max-width, `.ThemeGrid_Width*` columns, `.ThemeGrid_MarginGutter` spacing, `.active-screen` scroll model). Normally injected by OutSystems at runtime — we provide it statically. Never edit this.
|
|
369
451
|
3. **`theme.css`** — project-specific brand tokens and custom styles. This is the only file you should edit.
|
|
452
|
+
4. **`device-detect.js`** (in `scripts/`) — viewport detection script that applies `phone`/`tablet`/`desktop` + `portrait`/`landscape` classes to `<body>`. Required for responsive column breaks and `display-on-device-*` classes. Never edit this.
|
|
370
453
|
|
|
371
|
-
All three `<link>` tags must be present in `<head>`:
|
|
454
|
+
All three `<link>` tags must be present in `<head>`, and the script at the bottom of `<body>`:
|
|
372
455
|
```html
|
|
373
|
-
<
|
|
374
|
-
<link rel="stylesheet" href="../theme/
|
|
375
|
-
<link rel="stylesheet" href="../theme/
|
|
456
|
+
<head>
|
|
457
|
+
<link rel="stylesheet" href="../theme/outsystems-ui.css" />
|
|
458
|
+
<link rel="stylesheet" href="../theme/grid.css" />
|
|
459
|
+
<link rel="stylesheet" href="../theme/theme.css" />
|
|
460
|
+
</head>
|
|
461
|
+
<body>
|
|
462
|
+
<!-- ... page content ... -->
|
|
463
|
+
<script src="../scripts/device-detect.js"></script>
|
|
464
|
+
</body>
|
|
376
465
|
```
|
|
377
466
|
|
|
378
467
|
### What theme.css MUST contain
|
|
@@ -442,15 +531,22 @@ Follow this checklist when creating or editing a screen. Do not skip steps.
|
|
|
442
531
|
2. No inline `style=""` attributes anywhere
|
|
443
532
|
3. All classes are OSUI utilities OR custom classes defined in `theme.css`
|
|
444
533
|
4. All three CSS stylesheets linked in `<head>`: `outsystems-ui.css`, `grid.css`, `theme.css` (in that order)
|
|
445
|
-
5.
|
|
446
|
-
6.
|
|
447
|
-
7.
|
|
448
|
-
8.
|
|
534
|
+
5. `device-detect.js` script tag present at end of `<body>`
|
|
535
|
+
6. If Font Awesome icons are used, the FA CDN `<link>` is in `<head>`
|
|
536
|
+
7. No `@media` queries or custom breakpoints
|
|
537
|
+
8. Scroll model and grid are handled by `grid.css` (not duplicated in `theme.css`)
|
|
538
|
+
9. **Text readability** — for every text element, confirm the text color contrasts with its background:
|
|
449
539
|
- Dark backgrounds (`.background-primary`, `.background-neutral-10`, navy/dark sections) → use light text (`.text-neutral-0` or white custom class)
|
|
450
540
|
- Light backgrounds (`.background-neutral-0`, white sections) → use dark text (`.text-neutral-10` or default)
|
|
451
541
|
- **Header and menu links are especially prone** — if the header has a dark background, menu links MUST use a light text class. OSUI link defaults are the primary color, which may be invisible on a dark header.
|
|
452
542
|
- Buttons: check that button text contrasts with the button background (`.btn` defaults to white text — don't add `.text-neutral-0` on a white button)
|
|
453
|
-
|
|
543
|
+
10. **Responsive columns** — for every `.columns*` pattern, verify the break behavior:
|
|
544
|
+
- Every multi-column layout SHOULD have a `phone-break-*` class. Without one, columns stay side-by-side on phone, which is almost always wrong.
|
|
545
|
+
- Choose the right break: `phone-break-all` (stack everything) is the safe default. Use `phone-break-first`/`-last` when one column should stay full-width while others share a row.
|
|
546
|
+
- For tablet, add `tablet-break-*` only if the column count is 4+ or if side-by-side is too cramped at 768px.
|
|
547
|
+
- `break-middle` is the right choice for `columns4`–`columns6` on tablet — it goes to a 2- or 3-column grid instead of full-width stacking.
|
|
548
|
+
- Verify gutter class is present (`gutter-base` is the safe default).
|
|
549
|
+
11. If any check fails, fix and re-check before continuing
|
|
454
550
|
- [ ] Step 3: **Install dependencies** — if `node_modules/` does not exist, run `npm install`
|
|
455
551
|
- [ ] Step 4: **Start the dev server** — run `npm run dev` to serve the project at `http://localhost:5173/`
|
|
456
552
|
- [ ] Step 5: **Visual verification** — open the screen URL in the browser and check:
|
|
@@ -463,6 +559,7 @@ Follow this checklist when creating or editing a screen. Do not skip steps.
|
|
|
463
559
|
|
|
464
560
|
## Gotchas
|
|
465
561
|
|
|
562
|
+
- **Columns require TWO classes on the parent + `.columns-item` children** — The parent MUST have both `.columns` AND the number class: `class="columns columns3 gutter-base"`. The `.columns` class provides `display: flex` — without it, items stack vertically. Children MUST be `class="columns-item"` — NOT `class="column"` (that class does not exist in OSUI). Wrong: `<div class="columns3"><div class="column">`. Right: `<div class="columns columns3 gutter-base"><div class="columns-item">`.
|
|
466
563
|
- `.active-screen` is **REQUIRED** as the outermost wrapper. Without it, utility classes and CSS variables will NOT resolve. It also serves as the **scroll container** \u2014 OSUI sets `html { overflow: hidden }` so `.active-screen` must have `overflow-y: auto; height: 100vh` (provided by `grid.css`).\n- **`.ThemeGrid_MarginGutter` needs `grid.css`** \u2014 this class is used for menu link spacing and form field gutters, but its `margin-left` value is NOT in `outsystems-ui.css`. It comes from `grid.css`. Without it, menu links will bunch together with no spacing.
|
|
467
564
|
- **Button + color utilities don't mix** — `.btn` overrides `color` and `background-color` at high specificity. Stacking `.btn .background-neutral-0 .text-primary` will produce invisible text (white on white). Create custom button classes in `theme.css` instead.
|
|
468
565
|
- **Header menu links vanish on dark headers** — OSUI link color defaults to `var(--color-primary)`. On a dark header where `--color-primary` is also dark (e.g. navy), menu links become invisible. Fix: add a custom class in `theme.css` that forces light link color in the header (e.g. `.header__link { color: #fff; }`).
|
|
@@ -470,7 +567,8 @@ Follow this checklist when creating or editing a screen. Do not skip steps.
|
|
|
470
567
|
- **Font Awesome is not bundled** — If using `<i class="fa fa-exchange">` style icons, add the FA 4.7 CDN link to `<head>` or the icons will be invisible (0×0 size).
|
|
471
568
|
- OutSystems pattern previews render inside iframes — CSS resolves against the OSUI framework, not browser defaults. Your `.visual.html` must link the OSUI stylesheet.
|
|
472
569
|
- **Never mix** inline `style=""` attributes with utility classes. Always prefer utility classes.
|
|
473
|
-
- `.columns*` patterns auto-stack on mobile breakpoints. Do NOT add custom `@media` queries
|
|
570
|
+
- `.columns*` patterns auto-stack on mobile breakpoints **only if a break class is applied** (e.g. `phone-break-all`). Without a break class, columns stay side-by-side on all viewports. Do NOT add custom `@media` queries — use the break classes instead.
|
|
571
|
+
- **`device-detect.js` is required** — responsive column breaks and `display-on-device-*` classes are scoped to body classes (`phone`, `tablet`, `desktop`). Without the script, they have no effect in static previews.
|
|
474
572
|
- Button loading state is purely CSS (`.btn-loading` + spinner element), not a JavaScript toggle.
|
|
475
573
|
- Use `{{content}}` placeholders in component templates to indicate where nested child widgets go.
|
|
476
574
|
- Always generate BOTH the `.visual.html` AND corresponding `theme.css` styles together. A screen without proper theme styles will look broken.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="stylesheet" href="../theme/grid.css" />
|
|
9
9
|
<link rel="stylesheet" href="../theme/theme.css" />
|
|
10
10
|
</head>
|
|
11
|
-
<body
|
|
11
|
+
<body>
|
|
12
12
|
<!--
|
|
13
13
|
LayoutBase — Full-width sections, content-first.
|
|
14
14
|
Best for: landing pages, marketing pages, public-facing screens.
|
|
@@ -82,10 +82,10 @@
|
|
|
82
82
|
<!-- Feature Cards Section -->
|
|
83
83
|
<div data-block="Layouts.LayoutBaseSection" class="full-width-section padding-y-xxl background-neutral-0">
|
|
84
84
|
<div class="ThemeGrid_Container">
|
|
85
|
-
<div class="columns3">
|
|
86
|
-
<div class="
|
|
87
|
-
<div class="
|
|
88
|
-
<div class="
|
|
85
|
+
<div class="columns columns3 gutter-base phone-break-all">
|
|
86
|
+
<div class="columns-item">{{feature-1}}</div>
|
|
87
|
+
<div class="columns-item">{{feature-2}}</div>
|
|
88
|
+
<div class="columns-item">{{feature-3}}</div>
|
|
89
89
|
</div>
|
|
90
90
|
</div>
|
|
91
91
|
</div>
|
|
@@ -105,5 +105,6 @@
|
|
|
105
105
|
</div>
|
|
106
106
|
</div>
|
|
107
107
|
</div>
|
|
108
|
+
<script src="../scripts/device-detect.js"></script>
|
|
108
109
|
</body>
|
|
109
110
|
</html>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="stylesheet" href="../theme/grid.css" />
|
|
9
9
|
<link rel="stylesheet" href="../theme/theme.css" />
|
|
10
10
|
</head>
|
|
11
|
-
<body
|
|
11
|
+
<body>
|
|
12
12
|
<!--
|
|
13
13
|
LayoutBlank — No header, no footer, no navigation.
|
|
14
14
|
Best for: login pages, error pages, splash screens, or any screen
|
|
@@ -27,5 +27,6 @@
|
|
|
27
27
|
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
30
|
+
<script src="../scripts/device-detect.js"></script>
|
|
30
31
|
</body>
|
|
31
32
|
</html>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="stylesheet" href="../theme/grid.css" />
|
|
9
9
|
<link rel="stylesheet" href="../theme/theme.css" />
|
|
10
10
|
</head>
|
|
11
|
-
<body
|
|
11
|
+
<body>
|
|
12
12
|
<!--
|
|
13
13
|
LayoutSideMenu — Vertical navigation in a left sidebar.
|
|
14
14
|
Best for: backoffice/admin apps with many pages or deep navigation.
|
|
@@ -114,5 +114,6 @@
|
|
|
114
114
|
</div>
|
|
115
115
|
</div>
|
|
116
116
|
</div>
|
|
117
|
+
<script src="../scripts/device-detect.js"></script>
|
|
117
118
|
</body>
|
|
118
119
|
</html>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="stylesheet" href="../theme/grid.css" />
|
|
9
9
|
<link rel="stylesheet" href="../theme/theme.css" />
|
|
10
10
|
</head>
|
|
11
|
-
<body
|
|
11
|
+
<body>
|
|
12
12
|
<!--
|
|
13
13
|
LayoutTopMenu — Horizontal navigation bar at the top.
|
|
14
14
|
Best for: web apps with few top-level pages (3–6 menu items).
|
|
@@ -97,5 +97,6 @@
|
|
|
97
97
|
</div>
|
|
98
98
|
</div>
|
|
99
99
|
</div>
|
|
100
|
+
<script src="../scripts/device-detect.js"></script>
|
|
100
101
|
</body>
|
|
101
102
|
</html>
|
|
@@ -22,8 +22,8 @@ KPI cards on top, chart area in the middle, recent activity list at the bottom.
|
|
|
22
22
|
<!-- Goes inside .content-middle -->
|
|
23
23
|
<div class="main-content padding-base">
|
|
24
24
|
<!-- KPI Row -->
|
|
25
|
-
<div class="columns4 margin-bottom-l">
|
|
26
|
-
<div class="
|
|
25
|
+
<div class="columns columns4 gutter-base tablet-break-middle phone-break-all margin-bottom-l">
|
|
26
|
+
<div class="columns-item">
|
|
27
27
|
<div class="card padding-base">
|
|
28
28
|
<div class="counter">
|
|
29
29
|
<div class="counter-value font-size-display text-primary">{{kpi-1-value}}</div>
|
|
@@ -31,7 +31,7 @@ KPI cards on top, chart area in the middle, recent activity list at the bottom.
|
|
|
31
31
|
</div>
|
|
32
32
|
</div>
|
|
33
33
|
</div>
|
|
34
|
-
<div class="
|
|
34
|
+
<div class="columns-item">
|
|
35
35
|
<div class="card padding-base">
|
|
36
36
|
<div class="counter">
|
|
37
37
|
<div class="counter-value font-size-display text-success">{{kpi-2-value}}</div>
|
|
@@ -39,7 +39,7 @@ KPI cards on top, chart area in the middle, recent activity list at the bottom.
|
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
41
41
|
</div>
|
|
42
|
-
<div class="
|
|
42
|
+
<div class="columns-item">
|
|
43
43
|
<div class="card padding-base">
|
|
44
44
|
<div class="counter">
|
|
45
45
|
<div class="counter-value font-size-display text-warning">{{kpi-3-value}}</div>
|
|
@@ -47,7 +47,7 @@ KPI cards on top, chart area in the middle, recent activity list at the bottom.
|
|
|
47
47
|
</div>
|
|
48
48
|
</div>
|
|
49
49
|
</div>
|
|
50
|
-
<div class="
|
|
50
|
+
<div class="columns-item">
|
|
51
51
|
<div class="card padding-base">
|
|
52
52
|
<div class="counter">
|
|
53
53
|
<div class="counter-value font-size-display text-error">{{kpi-4-value}}</div>
|
|
@@ -58,8 +58,8 @@ KPI cards on top, chart area in the middle, recent activity list at the bottom.
|
|
|
58
58
|
</div>
|
|
59
59
|
|
|
60
60
|
<!-- Chart Area -->
|
|
61
|
-
<div class="columns2 margin-bottom-l">
|
|
62
|
-
<div class="
|
|
61
|
+
<div class="columns columns2 gutter-base phone-break-all margin-bottom-l">
|
|
62
|
+
<div class="columns-item">
|
|
63
63
|
<div class="card">
|
|
64
64
|
<div class="card-header padding-base">
|
|
65
65
|
<h5 class="margin-bottom-none">{{chart-1-title}}</h5>
|
|
@@ -67,7 +67,7 @@ KPI cards on top, chart area in the middle, recent activity list at the bottom.
|
|
|
67
67
|
<div class="card-content padding-base">{{chart-1-placeholder}}</div>
|
|
68
68
|
</div>
|
|
69
69
|
</div>
|
|
70
|
-
<div class="
|
|
70
|
+
<div class="columns-item">
|
|
71
71
|
<div class="card">
|
|
72
72
|
<div class="card-header padding-base">
|
|
73
73
|
<h5 class="margin-bottom-none">{{chart-2-title}}</h5>
|
|
@@ -101,11 +101,11 @@ Searchable table with pagination. The standard CRUD list view.
|
|
|
101
101
|
```html
|
|
102
102
|
<div class="layout active-screen">
|
|
103
103
|
<header class="header">
|
|
104
|
-
<div class="columns2">
|
|
105
|
-
<div class="
|
|
104
|
+
<div class="columns columns2 gutter-base phone-break-all">
|
|
105
|
+
<div class="columns-item">
|
|
106
106
|
<h1 class="font-size-h2 margin-bottom-none">{{entity-name-plural}}</h1>
|
|
107
107
|
</div>
|
|
108
|
-
<div class="
|
|
108
|
+
<div class="columns-item" style="text-align: right;">
|
|
109
109
|
<button class="btn btn-primary">Create New</button>
|
|
110
110
|
</div>
|
|
111
111
|
</div>
|
|
@@ -175,11 +175,11 @@ Read-only view of a single record with sections.
|
|
|
175
175
|
<span class="breadcrumb-separator">/</span>
|
|
176
176
|
<span class="breadcrumb-item active">{{record-name}}</span>
|
|
177
177
|
</div>
|
|
178
|
-
<div class="columns2">
|
|
179
|
-
<div class="
|
|
178
|
+
<div class="columns columns2 gutter-base phone-break-all">
|
|
179
|
+
<div class="columns-item">
|
|
180
180
|
<h1 class="font-size-h2 margin-bottom-none">{{record-name}}</h1>
|
|
181
181
|
</div>
|
|
182
|
-
<div class="
|
|
182
|
+
<div class="columns-item" style="text-align: right;">
|
|
183
183
|
<button class="btn">Edit</button>
|
|
184
184
|
<button class="btn btn-error">Delete</button>
|
|
185
185
|
</div>
|
|
@@ -193,8 +193,8 @@ Read-only view of a single record with sections.
|
|
|
193
193
|
<h5 class="section-title">General Information</h5>
|
|
194
194
|
</div>
|
|
195
195
|
<div class="section-content">
|
|
196
|
-
<div class="columns2">
|
|
197
|
-
<div class="
|
|
196
|
+
<div class="columns columns2 gutter-base phone-break-all">
|
|
197
|
+
<div class="columns-item">
|
|
198
198
|
<div class="margin-bottom-base">
|
|
199
199
|
<div class="font-size-s text-neutral-6">{{field-1-label}}</div>
|
|
200
200
|
<div>{{field-1-value}}</div>
|
|
@@ -204,7 +204,7 @@ Read-only view of a single record with sections.
|
|
|
204
204
|
<div>{{field-2-value}}</div>
|
|
205
205
|
</div>
|
|
206
206
|
</div>
|
|
207
|
-
<div class="
|
|
207
|
+
<div class="columns-item">
|
|
208
208
|
<div class="margin-bottom-base">
|
|
209
209
|
<div class="font-size-s text-neutral-6">{{field-3-label}}</div>
|
|
210
210
|
<div>{{field-3-value}}</div>
|
|
@@ -262,8 +262,8 @@ Create/edit form for a single record.
|
|
|
262
262
|
<main class="main-content padding-base">
|
|
263
263
|
<div class="card padding-l">
|
|
264
264
|
<form class="form">
|
|
265
|
-
<div class="columns2">
|
|
266
|
-
<div class="
|
|
265
|
+
<div class="columns columns2 gutter-base phone-break-all">
|
|
266
|
+
<div class="columns-item">
|
|
267
267
|
<div class="form-group">
|
|
268
268
|
<label class="form-label">{{field-1-label}}</label>
|
|
269
269
|
<input type="text" class="form-control" placeholder="{{field-1-placeholder}}" />
|
|
@@ -273,7 +273,7 @@ Create/edit form for a single record.
|
|
|
273
273
|
<input type="text" class="form-control" placeholder="{{field-2-placeholder}}" />
|
|
274
274
|
</div>
|
|
275
275
|
</div>
|
|
276
|
-
<div class="
|
|
276
|
+
<div class="columns-item">
|
|
277
277
|
<div class="form-group">
|
|
278
278
|
<label class="form-label">{{field-3-label}}</label>
|
|
279
279
|
<div class="dropdown">
|
|
@@ -11,7 +11,7 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
11
11
|
### Columns 2
|
|
12
12
|
|
|
13
13
|
```html
|
|
14
|
-
<div class="columns columns2 gutter-base">
|
|
14
|
+
<div class="columns columns2 gutter-base phone-break-all">
|
|
15
15
|
<div class="columns-item">Column 1</div>
|
|
16
16
|
<div class="columns-item">Column 2</div>
|
|
17
17
|
</div>
|
|
@@ -20,7 +20,7 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
20
20
|
### Columns 3
|
|
21
21
|
|
|
22
22
|
```html
|
|
23
|
-
<div class="columns columns3 gutter-base">
|
|
23
|
+
<div class="columns columns3 gutter-base phone-break-all">
|
|
24
24
|
<div class="columns-item">Column 1</div>
|
|
25
25
|
<div class="columns-item">Column 2</div>
|
|
26
26
|
<div class="columns-item">Column 3</div>
|
|
@@ -30,7 +30,7 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
30
30
|
### Columns 4
|
|
31
31
|
|
|
32
32
|
```html
|
|
33
|
-
<div class="columns columns4 gutter-base">
|
|
33
|
+
<div class="columns columns4 gutter-base tablet-break-middle phone-break-all">
|
|
34
34
|
<div class="columns-item">Column 1</div>
|
|
35
35
|
<div class="columns-item">Column 2</div>
|
|
36
36
|
<div class="columns-item">Column 3</div>
|
|
@@ -41,7 +41,7 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
41
41
|
### Columns 5
|
|
42
42
|
|
|
43
43
|
```html
|
|
44
|
-
<div class="columns columns5 gutter-base">
|
|
44
|
+
<div class="columns columns5 gutter-base tablet-break-middle phone-break-all">
|
|
45
45
|
<div class="columns-item">...</div>
|
|
46
46
|
<!-- 5 columns-item -->
|
|
47
47
|
</div>
|
|
@@ -50,7 +50,7 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
50
50
|
### Columns 6
|
|
51
51
|
|
|
52
52
|
```html
|
|
53
|
-
<div class="columns columns6 gutter-base">
|
|
53
|
+
<div class="columns columns6 gutter-base tablet-break-middle phone-break-all">
|
|
54
54
|
<div class="columns-item">...</div>
|
|
55
55
|
<!-- 6 columns-item -->
|
|
56
56
|
</div>
|
|
@@ -59,7 +59,7 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
59
59
|
### Columns Medium Left
|
|
60
60
|
|
|
61
61
|
```html
|
|
62
|
-
<div class="columns columns-medium-left gutter-base">
|
|
62
|
+
<div class="columns columns-medium-left gutter-base phone-break-all">
|
|
63
63
|
<div class="columns-item">Wider left</div>
|
|
64
64
|
<div class="columns-item">Narrower right</div>
|
|
65
65
|
</div>
|
|
@@ -68,7 +68,7 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
68
68
|
### Columns Medium Right
|
|
69
69
|
|
|
70
70
|
```html
|
|
71
|
-
<div class="columns columns-medium-right gutter-base">
|
|
71
|
+
<div class="columns columns-medium-right gutter-base phone-break-all">
|
|
72
72
|
<div class="columns-item">Narrower left</div>
|
|
73
73
|
<div class="columns-item">Wider right</div>
|
|
74
74
|
</div>
|
|
@@ -77,7 +77,7 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
77
77
|
### Columns Small Left
|
|
78
78
|
|
|
79
79
|
```html
|
|
80
|
-
<div class="columns columns-small-left gutter-base">
|
|
80
|
+
<div class="columns columns-small-left gutter-base phone-break-all">
|
|
81
81
|
<div class="columns-item">Small left</div>
|
|
82
82
|
<div class="columns-item">Large right</div>
|
|
83
83
|
</div>
|
|
@@ -86,12 +86,40 @@ Notation: `tag.class1.class2` for elements, `[Block.Name]` for OutSystems block
|
|
|
86
86
|
### Columns Small Right
|
|
87
87
|
|
|
88
88
|
```html
|
|
89
|
-
<div class="columns columns-small-right gutter-base">
|
|
89
|
+
<div class="columns columns-small-right gutter-base phone-break-all">
|
|
90
90
|
<div class="columns-item">Large left</div>
|
|
91
91
|
<div class="columns-item">Small right</div>
|
|
92
92
|
</div>
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
### Column Break Variants
|
|
96
|
+
|
|
97
|
+
Combine `tablet-break-*` and `phone-break-*` independently:
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<!-- First column breaks on phone, all stay on tablet -->
|
|
101
|
+
<div class="columns columns3 gutter-base phone-break-first">
|
|
102
|
+
<div class="columns-item">Sidebar (full-width on phone)</div>
|
|
103
|
+
<div class="columns-item">Main</div>
|
|
104
|
+
<div class="columns-item">Aside</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- 4-column grid: 2×2 on tablet, full-stack on phone -->
|
|
108
|
+
<div class="columns columns4 gutter-base tablet-break-middle phone-break-all">
|
|
109
|
+
<div class="columns-item">Card 1</div>
|
|
110
|
+
<div class="columns-item">Card 2</div>
|
|
111
|
+
<div class="columns-item">Card 3</div>
|
|
112
|
+
<div class="columns-item">Card 4</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<!-- Last column breaks on both tablet and phone -->
|
|
116
|
+
<div class="columns columns3 gutter-m tablet-break-last phone-break-all">
|
|
117
|
+
<div class="columns-item">Main</div>
|
|
118
|
+
<div class="columns-item">Secondary</div>
|
|
119
|
+
<div class="columns-item">Full-width footer row</div>
|
|
120
|
+
</div>
|
|
121
|
+
```
|
|
122
|
+
|
|
95
123
|
### Display On Device
|
|
96
124
|
|
|
97
125
|
Show/hide content per breakpoint.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>{{PROJECT_NAME}}</title>
|
|
7
|
+
<link rel="stylesheet" href="theme/outsystems-ui.css" />
|
|
8
|
+
<link rel="stylesheet" href="theme/grid.css" />
|
|
9
|
+
<link rel="stylesheet" href="theme/theme.css" />
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--w-fg: #171717;
|
|
13
|
+
--w-muted: #687076;
|
|
14
|
+
--w-bg: #ffffff;
|
|
15
|
+
--w-border: #e8e8e8;
|
|
16
|
+
--w-code-bg: #f2f2f2;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@media (prefers-color-scheme: dark) {
|
|
20
|
+
:root {
|
|
21
|
+
--w-fg: #ededed;
|
|
22
|
+
--w-muted: #8f8f8f;
|
|
23
|
+
--w-bg: #0a0a0a;
|
|
24
|
+
--w-border: #2e2e2e;
|
|
25
|
+
--w-code-bg: #1a1a1a;
|
|
26
|
+
}
|
|
27
|
+
.layout { background: var(--w-bg); }
|
|
28
|
+
.header { background: var(--w-bg) !important; border-bottom-color: var(--w-border) !important; }
|
|
29
|
+
.content { background: var(--w-bg); }
|
|
30
|
+
.heading6, span { color: var(--w-fg); }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.welcome-page {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
align-items: center;
|
|
37
|
+
justify-content: center;
|
|
38
|
+
min-height: calc(100vh - 120px);
|
|
39
|
+
padding: 2rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.welcome-top {
|
|
43
|
+
display: flex;
|
|
44
|
+
justify-content: space-between;
|
|
45
|
+
align-items: center;
|
|
46
|
+
width: 100%;
|
|
47
|
+
max-width: 1024px;
|
|
48
|
+
margin-bottom: 4rem;
|
|
49
|
+
font-size: 0.875rem;
|
|
50
|
+
color: var(--w-muted);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.welcome-top code {
|
|
54
|
+
font-family: 'SF Mono', 'Fira Code', 'Fira Mono', Menlo, Consolas, monospace;
|
|
55
|
+
font-size: 0.8125rem;
|
|
56
|
+
background: var(--w-code-bg);
|
|
57
|
+
border: 1px solid var(--w-border);
|
|
58
|
+
padding: 0.25rem 0.625rem;
|
|
59
|
+
border-radius: 6px;
|
|
60
|
+
color: var(--w-fg);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.welcome-top .by {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 0.375rem;
|
|
67
|
+
color: var(--w-muted);
|
|
68
|
+
font-size: 0.8125rem;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.welcome-top .by a {
|
|
72
|
+
color: var(--w-fg);
|
|
73
|
+
text-decoration: none;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.welcome-logo {
|
|
78
|
+
font-size: 4.5rem;
|
|
79
|
+
font-weight: 700;
|
|
80
|
+
letter-spacing: -0.05em;
|
|
81
|
+
color: var(--w-fg);
|
|
82
|
+
text-align: center;
|
|
83
|
+
margin-bottom: 4rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.welcome-logo span { font-weight: 300; font-size: 2rem; vertical-align: super; color: var(--w-muted); }
|
|
87
|
+
|
|
88
|
+
.welcome-links {
|
|
89
|
+
display: grid;
|
|
90
|
+
grid-template-columns: repeat(4, 1fr);
|
|
91
|
+
width: 100%;
|
|
92
|
+
max-width: 1024px;
|
|
93
|
+
border-top: 1px solid var(--w-border);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.welcome-link {
|
|
97
|
+
padding: 1.5rem 1.25rem;
|
|
98
|
+
text-decoration: none;
|
|
99
|
+
color: var(--w-fg);
|
|
100
|
+
border-left: 1px solid var(--w-border);
|
|
101
|
+
transition: background 0.15s;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.welcome-link:first-child { border-left: none; }
|
|
105
|
+
|
|
106
|
+
.welcome-link:hover { background: var(--w-code-bg); }
|
|
107
|
+
|
|
108
|
+
.welcome-link h2 {
|
|
109
|
+
font-size: 1rem;
|
|
110
|
+
font-weight: 600;
|
|
111
|
+
margin-bottom: 0.5rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.welcome-link:hover h2::after { margin-left: 4px; }
|
|
115
|
+
|
|
116
|
+
.welcome-link p {
|
|
117
|
+
font-size: 0.8125rem;
|
|
118
|
+
color: var(--w-muted);
|
|
119
|
+
line-height: 1.55;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@media (max-width: 640px) {
|
|
123
|
+
.welcome-links { grid-template-columns: 1fr; }
|
|
124
|
+
.welcome-link { border-left: none; border-bottom: 1px solid var(--w-border); }
|
|
125
|
+
.welcome-top { flex-direction: column; gap: 1rem; text-align: center; }
|
|
126
|
+
.welcome-logo { font-size: 3rem; }
|
|
127
|
+
}
|
|
128
|
+
</style>
|
|
129
|
+
</head>
|
|
130
|
+
<body>
|
|
131
|
+
<div class="active-screen">
|
|
132
|
+
<div data-block="Layouts.LayoutTopMenu" class="layout layout-top fixed-header">
|
|
133
|
+
<div class="main">
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
<!-- Content -->
|
|
137
|
+
<div class="content">
|
|
138
|
+
<div class="main-content" role="main">
|
|
139
|
+
|
|
140
|
+
<div class="welcome-page">
|
|
141
|
+
|
|
142
|
+
<div class="welcome-logo">flowmo</div>
|
|
143
|
+
|
|
144
|
+
<div class="welcome-links">
|
|
145
|
+
<a class="welcome-link" href="screens/home.visual.html">
|
|
146
|
+
<h2>Home</h2>
|
|
147
|
+
<p>Open the starter screen to see OutSystems UI patterns in action.</p>
|
|
148
|
+
</a>
|
|
149
|
+
<div class="welcome-link">
|
|
150
|
+
<h2>Screens</h2>
|
|
151
|
+
<p>Create .visual.html files in the screens/ folder.</p>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="welcome-link">
|
|
154
|
+
<h2>Logic</h2>
|
|
155
|
+
<p>Define actions as .flowchart.md files in logic/.</p>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="welcome-link">
|
|
158
|
+
<h2>Data</h2>
|
|
159
|
+
<p>Model entities as Markdown tables. Write SQL in data/sql/.</p>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
<script src="scripts/device-detect.js"></script>
|
|
171
|
+
</body>
|
|
172
|
+
</html>
|
|
@@ -7,8 +7,85 @@
|
|
|
7
7
|
<link rel="stylesheet" href="../theme/outsystems-ui.css">
|
|
8
8
|
<link rel="stylesheet" href="../theme/grid.css">
|
|
9
9
|
<link rel="stylesheet" href="../theme/theme.css">
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--w-fg: #171717;
|
|
13
|
+
--w-muted: #687076;
|
|
14
|
+
--w-bg: #ffffff;
|
|
15
|
+
--w-border: #e8e8e8;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@media (prefers-color-scheme: dark) {
|
|
19
|
+
:root {
|
|
20
|
+
--w-fg: #ededed;
|
|
21
|
+
--w-muted: #8f8f8f;
|
|
22
|
+
--w-bg: #0a0a0a;
|
|
23
|
+
--w-border: #2e2e2e;
|
|
24
|
+
}
|
|
25
|
+
.layout { background: var(--w-bg); }
|
|
26
|
+
.header { background: var(--w-bg) !important; border-bottom-color: var(--w-border) !important; }
|
|
27
|
+
.content { background: var(--w-bg); }
|
|
28
|
+
.footer { background: var(--w-bg) !important; }
|
|
29
|
+
.heading1, .heading6, .paragraph, span, a { color: var(--w-fg); }
|
|
30
|
+
.text-neutral-6 { color: var(--w-muted) !important; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.home-hero {
|
|
34
|
+
text-align: center;
|
|
35
|
+
padding: 4rem 1rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.home-hero .heading1 {
|
|
39
|
+
font-size: 2rem;
|
|
40
|
+
letter-spacing: -0.03em;
|
|
41
|
+
margin-bottom: 0.75rem;
|
|
42
|
+
color: var(--w-fg);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.home-hero .paragraph {
|
|
46
|
+
color: var(--w-muted);
|
|
47
|
+
font-size: 1rem;
|
|
48
|
+
max-width: 480px;
|
|
49
|
+
margin: 0 auto 2rem;
|
|
50
|
+
line-height: 1.6;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.home-steps {
|
|
54
|
+
max-width: 480px;
|
|
55
|
+
margin: 0 auto;
|
|
56
|
+
text-align: left;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.home-step {
|
|
60
|
+
padding: 0.875rem 0;
|
|
61
|
+
border-bottom: 1px solid var(--w-border);
|
|
62
|
+
display: flex;
|
|
63
|
+
gap: 0.75rem;
|
|
64
|
+
align-items: baseline;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.home-step:last-child { border-bottom: none; }
|
|
68
|
+
|
|
69
|
+
.home-step-num {
|
|
70
|
+
font-size: 0.75rem;
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
color: var(--w-muted);
|
|
73
|
+
flex-shrink: 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.home-step p {
|
|
77
|
+
font-size: 0.875rem;
|
|
78
|
+
color: var(--w-fg);
|
|
79
|
+
line-height: 1.5;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.home-step code {
|
|
83
|
+
font-size: 0.8125rem;
|
|
84
|
+
color: var(--w-muted);
|
|
85
|
+
}
|
|
86
|
+
</style>
|
|
10
87
|
</head>
|
|
11
|
-
<body
|
|
88
|
+
<body>
|
|
12
89
|
<div class="active-screen">
|
|
13
90
|
<div data-block="Layouts.LayoutTopMenu" class="layout layout-top fixed-header">
|
|
14
91
|
<div class="main">
|
|
@@ -49,18 +126,33 @@
|
|
|
49
126
|
<!-- Content -->
|
|
50
127
|
<div class="content">
|
|
51
128
|
<div class="main-content ThemeGrid_Container" role="main">
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
129
|
+
|
|
130
|
+
<div class="home-hero">
|
|
131
|
+
<div class="heading1">Welcome to your app</div>
|
|
132
|
+
<p class="paragraph">This is a starter screen. Edit it, duplicate it, or replace it entirely.</p>
|
|
133
|
+
|
|
134
|
+
<div class="home-steps">
|
|
135
|
+
<div class="home-step">
|
|
136
|
+
<span class="home-step-num">01</span>
|
|
137
|
+
<p>Create screens as <code>.visual.html</code> files in <code>screens/</code>.</p>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="home-step">
|
|
140
|
+
<span class="home-step-num">02</span>
|
|
141
|
+
<p>Define actions as <code>.flowchart.md</code> files in <code>logic/</code>.</p>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="home-step">
|
|
144
|
+
<span class="home-step-num">03</span>
|
|
145
|
+
<p>Document your database in <code>data/</code> and write advanced queries in <code>data/sql/</code>.</p>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
57
148
|
</div>
|
|
149
|
+
|
|
58
150
|
</div>
|
|
59
151
|
|
|
60
152
|
<!-- Footer -->
|
|
61
153
|
<footer role="contentinfo" class="content-bottom">
|
|
62
154
|
<div class="footer ThemeGrid_Container">
|
|
63
|
-
<p class="paragraph text-neutral-6">
|
|
155
|
+
<p class="paragraph text-neutral-6">Powered by Flowmo</p>
|
|
64
156
|
</div>
|
|
65
157
|
</footer>
|
|
66
158
|
</div>
|
|
@@ -68,5 +160,6 @@
|
|
|
68
160
|
</div>
|
|
69
161
|
</div>
|
|
70
162
|
</div>
|
|
163
|
+
<script src="../scripts/device-detect.js"></script>
|
|
71
164
|
</body>
|
|
72
165
|
</html>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* ── OutSystems Device Class Detection ────────────────
|
|
2
|
+
In the real OutSystems platform, a runtime script detects
|
|
3
|
+
the viewport size and applies device + orientation classes
|
|
4
|
+
to <body>. Since we render static .visual.html files
|
|
5
|
+
outside the platform, this script replicates that behavior.
|
|
6
|
+
|
|
7
|
+
Breakpoints (matches OS runtime):
|
|
8
|
+
phone: width < 768
|
|
9
|
+
tablet: 768 ≤ width ≤ 1024
|
|
10
|
+
desktop: width > 1024
|
|
11
|
+
|
|
12
|
+
Orientation:
|
|
13
|
+
landscape: width > height
|
|
14
|
+
portrait: width ≤ height
|
|
15
|
+
|
|
16
|
+
Usage: add <script src="../theme/device-detect.js"></script>
|
|
17
|
+
at the end of <body>, AFTER the HTML content.
|
|
18
|
+
──────────────────────────────────────────────────────── */
|
|
19
|
+
(function () {
|
|
20
|
+
var PHONE = 'phone';
|
|
21
|
+
var TABLET = 'tablet';
|
|
22
|
+
var DESKTOP = 'desktop';
|
|
23
|
+
var PORTRAIT = 'portrait';
|
|
24
|
+
var LANDSCAPE = 'landscape';
|
|
25
|
+
var ALL_CLASSES = [PHONE, TABLET, DESKTOP, PORTRAIT, LANDSCAPE];
|
|
26
|
+
|
|
27
|
+
function applyDeviceClasses() {
|
|
28
|
+
var w = window.innerWidth || document.documentElement.clientWidth;
|
|
29
|
+
var h = window.innerHeight || document.documentElement.clientHeight;
|
|
30
|
+
var device = w < 768 ? PHONE : w <= 1024 ? TABLET : DESKTOP;
|
|
31
|
+
var orientation = w > h ? LANDSCAPE : PORTRAIT;
|
|
32
|
+
var body = document.body;
|
|
33
|
+
|
|
34
|
+
ALL_CLASSES.forEach(function (c) { body.classList.remove(c); });
|
|
35
|
+
body.classList.add(device, orientation);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
applyDeviceClasses();
|
|
39
|
+
window.addEventListener('resize', applyDeviceClasses);
|
|
40
|
+
})();
|