tux-review 0.1.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.
- package/README.md +67 -0
- package/bin/tux.js +17 -0
- package/client/tux-review.css +155 -0
- package/client/tux-review.js +586 -0
- package/package.json +59 -0
- package/skills/tux-design-create.md +50 -0
- package/skills/tux-design-incorporate.md +61 -0
- package/skills/tux-design-install.md +56 -0
- package/skills/tux-design-start-review.md +51 -0
- package/skills/tux-design-stop-review.md +44 -0
- package/skills/tux-feedback-delete.md +45 -0
- package/skills/tux-feedback-export.md +47 -0
- package/skills/tux-feedback-show.md +55 -0
- package/skills/tux-live-create.md +50 -0
- package/skills/tux-live-incorporate.md +62 -0
- package/skills/tux-live-install.md +63 -0
- package/skills/tux-live-start-review.md +47 -0
- package/skills/tux-live-stop-review.md +46 -0
- package/src/canonical.js +28 -0
- package/src/cli.js +229 -0
- package/src/config.js +72 -0
- package/src/design.js +140 -0
- package/src/errors.js +28 -0
- package/src/feedback.js +289 -0
- package/src/ids.js +57 -0
- package/src/index.js +17 -0
- package/src/live.js +261 -0
- package/src/schema.js +77 -0
- package/src/serve-main.js +42 -0
- package/src/server.js +347 -0
- package/src/store.js +72 -0
- package/src/templates.js +44 -0
- package/templates/angular/README.md +19 -0
- package/templates/angular/angular.json +49 -0
- package/templates/angular/package.json +25 -0
- package/templates/angular/src/app/app.component.html +40 -0
- package/templates/angular/src/app/app.component.ts +30 -0
- package/templates/angular/src/index.html +11 -0
- package/templates/angular/src/main.ts +4 -0
- package/templates/angular/src/styles.css +11 -0
- package/templates/angular/tsconfig.app.json +8 -0
- package/templates/angular/tsconfig.json +11 -0
- package/templates/react/README.md +18 -0
- package/templates/react/index.html +12 -0
- package/templates/react/package.json +18 -0
- package/templates/react/src/App.jsx +80 -0
- package/templates/react/src/main.jsx +6 -0
- package/templates/react/src/styles.css +11 -0
- package/templates/vanilla/README.md +22 -0
- package/templates/vanilla/index.html +18 -0
- package/templates/vanilla/package.json +6 -0
- package/templates/vanilla/src/app.js +79 -0
- package/templates/vanilla/src/styles.css +11 -0
- package/templates/vue/README.md +18 -0
- package/templates/vue/index.html +12 -0
- package/templates/vue/package.json +17 -0
- package/templates/vue/src/App.vue +61 -0
- package/templates/vue/src/main.js +5 -0
- package/templates/vue/src/styles.css +11 -0
- package/templates/vue/vite.config.js +6 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<nav data-tux-component="MainNav">
|
|
2
|
+
<a href="/" (click)="navigate('/', $event)">Home</a>
|
|
3
|
+
<a href="/products" (click)="navigate('/products', $event)">Products</a>
|
|
4
|
+
<a href="/checkout" (click)="navigate('/checkout', $event)">Checkout</a>
|
|
5
|
+
</nav>
|
|
6
|
+
<main>
|
|
7
|
+
@if (route === '/products') {
|
|
8
|
+
<h1>Products</h1>
|
|
9
|
+
<div data-tux-component="ProductTabs" [attr.data-tux-state-tab]="tab">
|
|
10
|
+
<div role="tablist">
|
|
11
|
+
@for (t of tabs; track t) {
|
|
12
|
+
<button role="tab" [attr.aria-selected]="tab === t" [attr.data-tab]="t" (click)="tab = t">{{ t }}</button>
|
|
13
|
+
}
|
|
14
|
+
</div>
|
|
15
|
+
<ul data-tux-component="ProductList" [style.display]="tab === 'featured' ? 'none' : ''">
|
|
16
|
+
@for (p of products; track p.id) {
|
|
17
|
+
<li data-tux-component="ProductCard" [attr.data-tux-instance]="p.id">
|
|
18
|
+
<span [attr.data-tux-id]="'product-name-' + p.id">{{ p.name }}</span>
|
|
19
|
+
<span [attr.data-tux-id]="'product-price-' + p.id" data-tux-component="PriceLabel">{{ p.price }}</span>
|
|
20
|
+
</li>
|
|
21
|
+
}
|
|
22
|
+
</ul>
|
|
23
|
+
</div>
|
|
24
|
+
} @else if (route === '/checkout') {
|
|
25
|
+
<h1>Checkout</h1>
|
|
26
|
+
<div data-tux-component="CheckoutActions">
|
|
27
|
+
<button data-tux-id="coupon-open" (click)="modalOpen = true">Add coupon</button>
|
|
28
|
+
<button data-tux-id="checkout-submit" data-tux-component="SubmitButton">Complete purchase</button>
|
|
29
|
+
</div>
|
|
30
|
+
@if (modalOpen) {
|
|
31
|
+
<div role="dialog" aria-modal="true" aria-label="Coupon" data-tux-component="CouponModal">
|
|
32
|
+
<p>Add a coupon code.</p>
|
|
33
|
+
<input data-tux-id="coupon-input" placeholder="Code" />
|
|
34
|
+
<button data-tux-id="coupon-apply" (click)="modalOpen = false">Apply</button>
|
|
35
|
+
</div>
|
|
36
|
+
}
|
|
37
|
+
} @else {
|
|
38
|
+
<h1>Home</h1>
|
|
39
|
+
}
|
|
40
|
+
</main>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: 'app-root',
|
|
5
|
+
standalone: true,
|
|
6
|
+
templateUrl: './app.component.html',
|
|
7
|
+
})
|
|
8
|
+
export class AppComponent {
|
|
9
|
+
readonly tabs = ['all', 'featured'];
|
|
10
|
+
readonly products = [
|
|
11
|
+
{ id: 'p1', name: 'Trail Shoes', price: '119 €' },
|
|
12
|
+
{ id: 'p2', name: 'Rain Jacket', price: '89 €' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
route = window.location.pathname;
|
|
16
|
+
tab = 'all';
|
|
17
|
+
modalOpen = false;
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
window.onpopstate = () => {
|
|
21
|
+
this.route = window.location.pathname;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
navigate(href: string, event: Event): void {
|
|
26
|
+
event.preventDefault();
|
|
27
|
+
window.history.pushState(null, '', href);
|
|
28
|
+
this.route = href;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* Clickable design styles (minimal). */
|
|
2
|
+
:root { font-family: system-ui, sans-serif; }
|
|
3
|
+
body { margin: 0; color: #1e1e1e; }
|
|
4
|
+
nav { display: flex; gap: 16px; padding: 12px 20px; border-bottom: 1px solid rgba(0,0,0,.12); }
|
|
5
|
+
main { padding: 20px; }
|
|
6
|
+
[role=tablist] { display: flex; gap: 8px; margin-bottom: 12px; }
|
|
7
|
+
[role=tab] { padding: 6px 14px; border: 1px solid rgba(0,0,0,.12); background: #fff; border-radius: 999px; cursor: pointer; }
|
|
8
|
+
[role=tab][aria-selected=true] { background: #569cd6; color: #fff; border-color: #569cd6; }
|
|
9
|
+
li { margin: 8px 0; display: flex; gap: 12px; }
|
|
10
|
+
[role=dialog] { border: 1px solid rgba(0,0,0,.12); border-radius: 12px; padding: 20px; }
|
|
11
|
+
button { font: inherit; }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# checkout (react design)
|
|
2
|
+
|
|
3
|
+
This design itself uses npm. Choose one TUX CLI: Node users run
|
|
4
|
+
`npm install --save-dev tux-review` then `npx tux …`; Python users install
|
|
5
|
+
`tux-review` with `pipx` or `pip` then run `tux …`. The TUX commands below are
|
|
6
|
+
otherwise identical.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install
|
|
10
|
+
|
|
11
|
+
# Node.js/npm TUX variant — serves the design with TUX review
|
|
12
|
+
npx tux design start-review --foreground --dir .
|
|
13
|
+
|
|
14
|
+
# Python/PyPI TUX variant — same command after Python installation
|
|
15
|
+
tux design start-review --foreground --dir .
|
|
16
|
+
|
|
17
|
+
# Alternative for either variant: npm run dev + <tux command> live start-review --url http://localhost:5173
|
|
18
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
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">
|
|
6
|
+
<title>Checkout Design</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.jsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tux-design",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite --port 5173",
|
|
8
|
+
"build": "vite build"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"react": "^18.3.1",
|
|
12
|
+
"react-dom": "^18.3.1"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
16
|
+
"vite": "^6.0.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const PRODUCTS = [
|
|
4
|
+
{ id: 'p1', name: 'Trail Shoes', price: '119 €' },
|
|
5
|
+
{ id: 'p2', name: 'Rain Jacket', price: '89 €' },
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
function ProductCard({ product }) {
|
|
9
|
+
return (
|
|
10
|
+
<li data-tux-component="ProductCard" data-tux-instance={product.id}>
|
|
11
|
+
<span data-tux-id={`product-name-${product.id}`}>{product.name}</span>
|
|
12
|
+
<span data-tux-id={`product-price-${product.id}`} data-tux-component="PriceLabel">
|
|
13
|
+
{product.price}
|
|
14
|
+
</span>
|
|
15
|
+
</li>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function CouponModal({ open, onClose }) {
|
|
20
|
+
if (!open) return null;
|
|
21
|
+
return (
|
|
22
|
+
<div role="dialog" aria-modal="true" aria-label="Coupon" data-tux-component="CouponModal">
|
|
23
|
+
<p>Add a coupon code.</p>
|
|
24
|
+
<input data-tux-id="coupon-input" placeholder="Code" />
|
|
25
|
+
<button data-tux-id="coupon-apply" onClick={onClose}>Apply</button>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default function App() {
|
|
31
|
+
const [route, setRoute] = useState(window.location.pathname);
|
|
32
|
+
const [tab, setTab] = useState('all');
|
|
33
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
34
|
+
|
|
35
|
+
window.onpopstate = () => setRoute(window.location.pathname);
|
|
36
|
+
const navigate = (href) => (e) => {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
window.history.pushState(null, '', href);
|
|
39
|
+
setRoute(href);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<nav data-tux-component="MainNav">
|
|
45
|
+
<a href="/" onClick={navigate('/')}>Home</a>
|
|
46
|
+
<a href="/products" onClick={navigate('/products')}>Products</a>
|
|
47
|
+
<a href="/checkout" onClick={navigate('/checkout')}>Checkout</a>
|
|
48
|
+
</nav>
|
|
49
|
+
<main>
|
|
50
|
+
{route === '/products' && (
|
|
51
|
+
<>
|
|
52
|
+
<h1>Products</h1>
|
|
53
|
+
<div data-tux-component="ProductTabs" data-tux-state-tab={tab}>
|
|
54
|
+
<div role="tablist">
|
|
55
|
+
{['all', 'featured'].map((t) => (
|
|
56
|
+
<button key={t} role="tab" aria-selected={tab === t} data-tab={t}
|
|
57
|
+
onClick={() => setTab(t)}>{t}</button>
|
|
58
|
+
))}
|
|
59
|
+
</div>
|
|
60
|
+
<ul data-tux-component="ProductList" style={tab === 'featured' ? { display: 'none' } : {}}>
|
|
61
|
+
{PRODUCTS.map((p) => <ProductCard key={p.id} product={p} />)}
|
|
62
|
+
</ul>
|
|
63
|
+
</div>
|
|
64
|
+
</>
|
|
65
|
+
)}
|
|
66
|
+
{route === '/checkout' && (
|
|
67
|
+
<>
|
|
68
|
+
<h1>Checkout</h1>
|
|
69
|
+
<div data-tux-component="CheckoutActions">
|
|
70
|
+
<button data-tux-id="coupon-open" onClick={() => setModalOpen(true)}>Add coupon</button>
|
|
71
|
+
<button data-tux-id="checkout-submit" data-tux-component="SubmitButton">Complete purchase</button>
|
|
72
|
+
</div>
|
|
73
|
+
<CouponModal open={modalOpen} onClose={() => setModalOpen(false)} />
|
|
74
|
+
</>
|
|
75
|
+
)}
|
|
76
|
+
{route === '/' && <h1>Home</h1>}
|
|
77
|
+
</main>
|
|
78
|
+
</>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* Clickable design styles (minimal). */
|
|
2
|
+
:root { font-family: system-ui, sans-serif; }
|
|
3
|
+
body { margin: 0; color: #1e1e1e; }
|
|
4
|
+
nav { display: flex; gap: 16px; padding: 12px 20px; border-bottom: 1px solid rgba(0,0,0,.12); }
|
|
5
|
+
main { padding: 20px; }
|
|
6
|
+
[role=tablist] { display: flex; gap: 8px; margin-bottom: 12px; }
|
|
7
|
+
[role=tab] { padding: 6px 14px; border: 1px solid rgba(0,0,0,.12); background: #fff; border-radius: 999px; cursor: pointer; }
|
|
8
|
+
[role=tab][aria-selected=true] { background: #569cd6; color: #fff; border-color: #569cd6; }
|
|
9
|
+
li { margin: 8px 0; display: flex; gap: 12px; }
|
|
10
|
+
[role=dialog] { border: 1px solid rgba(0,0,0,.12); border-radius: 12px; padding: 20px; }
|
|
11
|
+
button { font: inherit; }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# checkout (vanilla design)
|
|
2
|
+
|
|
3
|
+
## Choose the TUX CLI
|
|
4
|
+
|
|
5
|
+
Choose one variant; the commands are identical. Node.js users install TUX in
|
|
6
|
+
this project with `npm install --save-dev tux-review` and run
|
|
7
|
+
`npx tux …`. Python users install `tux-review` with `pipx install tux-review`
|
|
8
|
+
(or `python -m pip install tux-review`) and run `tux …`. You do not need both.
|
|
9
|
+
|
|
10
|
+
## Start the review
|
|
11
|
+
|
|
12
|
+
From the project root (or this directory):
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Node.js/npm variant
|
|
16
|
+
npx tux design start-review --foreground --dir .
|
|
17
|
+
|
|
18
|
+
# Python/PyPI variant
|
|
19
|
+
tux design start-review --foreground --dir .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Routes: `/`, `/products` (tabs), `/checkout` (modal).
|
|
@@ -0,0 +1,18 @@
|
|
|
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">
|
|
6
|
+
<title>Checkout Design</title>
|
|
7
|
+
<link rel="stylesheet" href="src/styles.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<nav data-tux-component="MainNav">
|
|
11
|
+
<a href="/" data-tux-id="nav-home">Home</a>
|
|
12
|
+
<a href="/products" data-tux-id="nav-products">Products</a>
|
|
13
|
+
<a href="/checkout" data-tux-id="nav-checkout">Checkout</a>
|
|
14
|
+
</nav>
|
|
15
|
+
<main id="view"></main>
|
|
16
|
+
<script type="module" src="src/app.js"></script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Clickable design (vanilla SPA with History-API routing).
|
|
2
|
+
const view = document.getElementById('view');
|
|
3
|
+
|
|
4
|
+
const PRODUCTS = [
|
|
5
|
+
{ id: 'p1', name: 'Trail Shoes', price: '119 €' },
|
|
6
|
+
{ id: 'p2', name: 'Rain Jacket', price: '89 €' },
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
function productsPage() {
|
|
10
|
+
return `
|
|
11
|
+
<h1>Products</h1>
|
|
12
|
+
<div data-tux-component="ProductTabs" data-tux-state-tab="all">
|
|
13
|
+
<div role="tablist">
|
|
14
|
+
<button role="tab" aria-selected="true" data-tab="all">All</button>
|
|
15
|
+
<button role="tab" aria-selected="false" data-tab="featured">Featured</button>
|
|
16
|
+
</div>
|
|
17
|
+
<ul id="product-list" data-tux-component="ProductList">
|
|
18
|
+
${PRODUCTS.map((p) => `
|
|
19
|
+
<li data-tux-component="ProductCard" data-tux-instance="${p.id}">
|
|
20
|
+
<span data-tux-id="product-name-${p.id}">${p.name}</span>
|
|
21
|
+
<span data-tux-id="product-price-${p.id}" data-tux-component="PriceLabel">${p.price}</span>
|
|
22
|
+
</li>`).join('')}
|
|
23
|
+
</ul>
|
|
24
|
+
</div>`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function checkoutPage() {
|
|
28
|
+
return `
|
|
29
|
+
<h1>Checkout</h1>
|
|
30
|
+
<div data-tux-component="CheckoutActions">
|
|
31
|
+
<button data-tux-id="coupon-open">Add coupon</button>
|
|
32
|
+
<button data-tux-id="checkout-submit" data-tux-component="SubmitButton">Complete purchase</button>
|
|
33
|
+
</div>
|
|
34
|
+
<dialog id="coupon-modal" data-tux-component="CouponModal">
|
|
35
|
+
<p>Add a coupon code.</p>
|
|
36
|
+
<input data-tux-id="coupon-input" placeholder="Code">
|
|
37
|
+
<button data-tux-id="coupon-apply">Apply</button>
|
|
38
|
+
</dialog>`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function homePage() {
|
|
42
|
+
return '<h1>Home</h1><p>Clickable TUX design.</p>';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function render() {
|
|
46
|
+
const route = location.pathname;
|
|
47
|
+
if (route === '/products') view.innerHTML = productsPage();
|
|
48
|
+
else if (route === '/checkout') view.innerHTML = checkoutPage();
|
|
49
|
+
else view.innerHTML = homePage();
|
|
50
|
+
bind();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function bind() {
|
|
54
|
+
view.querySelectorAll('[data-tab]').forEach((btn) => {
|
|
55
|
+
btn.addEventListener('click', () => {
|
|
56
|
+
btn.closest('[role=tablist]').querySelectorAll('[role=tab]').forEach((t) => {
|
|
57
|
+
t.setAttribute('aria-selected', String(t === btn));
|
|
58
|
+
});
|
|
59
|
+
const wrap = btn.closest('[data-tux-component]');
|
|
60
|
+
wrap.dataset.tuxStateTab = btn.dataset.tab;
|
|
61
|
+
const list = document.getElementById('product-list');
|
|
62
|
+
list.style.display = btn.dataset.tab === 'featured' ? 'none' : '';
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
const open = document.querySelector('[data-tux-id=coupon-open]');
|
|
66
|
+
const modal = document.getElementById('coupon-modal');
|
|
67
|
+
if (open && modal) open.addEventListener('click', () => modal.showModal());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
document.addEventListener('click', (e) => {
|
|
71
|
+
const a = e.target.closest('a[href^="/"]');
|
|
72
|
+
if (!a) return;
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
history.pushState(null, '', a.getAttribute('href'));
|
|
75
|
+
render();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
window.addEventListener('popstate', render);
|
|
79
|
+
render();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* Clickable design styles (minimal). */
|
|
2
|
+
:root { font-family: system-ui, sans-serif; }
|
|
3
|
+
body { margin: 0; color: #1e1e1e; }
|
|
4
|
+
nav { display: flex; gap: 16px; padding: 12px 20px; border-bottom: 1px solid rgba(0,0,0,.12); }
|
|
5
|
+
main { padding: 20px; }
|
|
6
|
+
[role=tablist] { display: flex; gap: 8px; margin-bottom: 12px; }
|
|
7
|
+
[role=tab] { padding: 6px 14px; border: 1px solid rgba(0,0,0,.12); background: #fff; border-radius: 999px; cursor: pointer; }
|
|
8
|
+
[role=tab][aria-selected=true] { background: #569cd6; color: #fff; border-color: #569cd6; }
|
|
9
|
+
li { margin: 8px 0; display: flex; gap: 12px; }
|
|
10
|
+
dialog { border: 1px solid rgba(0,0,0,.12); border-radius: 12px; padding: 20px; }
|
|
11
|
+
button { font: inherit; }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# checkout (vue design)
|
|
2
|
+
|
|
3
|
+
This design itself uses npm. Choose one TUX CLI: Node users run
|
|
4
|
+
`npm install --save-dev tux-review` then `npx tux …`; Python users install
|
|
5
|
+
`tux-review` with `pipx` or `pip` then run `tux …`. The TUX commands below are
|
|
6
|
+
otherwise identical.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install
|
|
10
|
+
|
|
11
|
+
# Node.js/npm TUX variant — serves the design with TUX review
|
|
12
|
+
npx tux design start-review --foreground --dir .
|
|
13
|
+
|
|
14
|
+
# Python/PyPI TUX variant — same command after Python installation
|
|
15
|
+
tux design start-review --foreground --dir .
|
|
16
|
+
|
|
17
|
+
# Alternative for either variant: npm run dev + <tux command> live start-review --url http://localhost:5173
|
|
18
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
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">
|
|
6
|
+
<title>Checkout Design</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="/src/main.js"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tux-design",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite --port 5173",
|
|
8
|
+
"build": "vite build"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"vue": "^3.5.13"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
15
|
+
"vite": "^6.0.0"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
const PRODUCTS = [
|
|
5
|
+
{ id: 'p1', name: 'Trail Shoes', price: '119 €' },
|
|
6
|
+
{ id: 'p2', name: 'Rain Jacket', price: '89 €' },
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
const route = ref(window.location.pathname);
|
|
10
|
+
const tab = ref('all');
|
|
11
|
+
const modalOpen = ref(false);
|
|
12
|
+
|
|
13
|
+
window.onpopstate = () => {
|
|
14
|
+
route.value = window.location.pathname;
|
|
15
|
+
};
|
|
16
|
+
const navigate = (href) => (event) => {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
window.history.pushState(null, '', href);
|
|
19
|
+
route.value = href;
|
|
20
|
+
};
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<nav data-tux-component="MainNav">
|
|
25
|
+
<a href="/" @click="navigate('/')">Home</a>
|
|
26
|
+
<a href="/products" @click="navigate('/products')">Products</a>
|
|
27
|
+
<a href="/checkout" @click="navigate('/checkout')">Checkout</a>
|
|
28
|
+
</nav>
|
|
29
|
+
<main>
|
|
30
|
+
<template v-if="route === '/products'">
|
|
31
|
+
<h1>Products</h1>
|
|
32
|
+
<div data-tux-component="ProductTabs" :data-tux-state-tab="tab">
|
|
33
|
+
<div role="tablist">
|
|
34
|
+
<button v-for="t in ['all', 'featured']" :key="t" role="tab"
|
|
35
|
+
:aria-selected="tab === t" :data-tab="t" @click="tab = t">{{ t }}</button>
|
|
36
|
+
</div>
|
|
37
|
+
<ul data-tux-component="ProductList" :style="tab === 'featured' ? { display: 'none' } : {}">
|
|
38
|
+
<li v-for="p in PRODUCTS" :key="p.id" data-tux-component="ProductCard" :data-tux-instance="p.id">
|
|
39
|
+
<span :data-tux-id="`product-name-${p.id}`">{{ p.name }}</span>
|
|
40
|
+
<span :data-tux-id="`product-price-${p.id}`" data-tux-component="PriceLabel">{{ p.price }}</span>
|
|
41
|
+
</li>
|
|
42
|
+
</ul>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
<template v-else-if="route === '/checkout'">
|
|
46
|
+
<h1>Checkout</h1>
|
|
47
|
+
<div data-tux-component="CheckoutActions">
|
|
48
|
+
<button data-tux-id="coupon-open" @click="modalOpen = true">Add coupon</button>
|
|
49
|
+
<button data-tux-id="checkout-submit" data-tux-component="SubmitButton">Complete purchase</button>
|
|
50
|
+
</div>
|
|
51
|
+
<div v-if="modalOpen" role="dialog" aria-modal="true" aria-label="Coupon" data-tux-component="CouponModal">
|
|
52
|
+
<p>Add a coupon code.</p>
|
|
53
|
+
<input data-tux-id="coupon-input" placeholder="Code" />
|
|
54
|
+
<button data-tux-id="coupon-apply" @click="modalOpen = false">Apply</button>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
<template v-else>
|
|
58
|
+
<h1>Home</h1>
|
|
59
|
+
</template>
|
|
60
|
+
</main>
|
|
61
|
+
</template>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* Clickable design styles (minimal). */
|
|
2
|
+
:root { font-family: system-ui, sans-serif; }
|
|
3
|
+
body { margin: 0; color: #1e1e1e; }
|
|
4
|
+
nav { display: flex; gap: 16px; padding: 12px 20px; border-bottom: 1px solid rgba(0,0,0,.12); }
|
|
5
|
+
main { padding: 20px; }
|
|
6
|
+
[role=tablist] { display: flex; gap: 8px; margin-bottom: 12px; }
|
|
7
|
+
[role=tab] { padding: 6px 14px; border: 1px solid rgba(0,0,0,.12); background: #fff; border-radius: 999px; cursor: pointer; }
|
|
8
|
+
[role=tab][aria-selected=true] { background: #569cd6; color: #fff; border-color: #569cd6; }
|
|
9
|
+
li { margin: 8px 0; display: flex; gap: 12px; }
|
|
10
|
+
[role=dialog] { border: 1px solid rgba(0,0,0,.12); border-radius: 12px; padding: 20px; }
|
|
11
|
+
button { font: inherit; }
|