@pfern/create-elements 1.0.1 → 1.0.3
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 +9 -1
- package/package.json +6 -3
- package/template/README.md +19 -267
- package/template/index.html +11 -3
- package/template/package.json +2 -2
- package/template/src/components/app.js +50 -0
- package/template/src/components/counter.js +2 -3
- package/template/src/components/todos.js +1 -2
- package/template/src/index.js +4 -15
- package/template/src/router.js +40 -0
- package/template/src/style.css +15 -26
- package/template/package-lock.json +0 -2177
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# create-elements
|
|
2
2
|
|
|
3
|
-
A CLI package to generate a minimal
|
|
3
|
+
A CLI package to generate a minimal Vite app using `@pfern/elements`.
|
|
4
|
+
|
|
5
|
+
If you’re looking for the core UI library docs, see the `@pfern/elements`
|
|
6
|
+
README:
|
|
7
|
+
|
|
8
|
+
- https://github.com/pfernandez/elements
|
|
4
9
|
|
|
5
10
|
## Usage
|
|
6
11
|
|
|
@@ -21,3 +26,6 @@ npm install
|
|
|
21
26
|
npm run dev
|
|
22
27
|
```
|
|
23
28
|
|
|
29
|
+
## Related
|
|
30
|
+
|
|
31
|
+
- `@pfern/elements`: https://github.com/pfernandez/elements
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pfern/create-elements",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A CLI script to generate a starter app with elements.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"type": "module",
|
|
20
20
|
"bin": {
|
|
21
|
-
"create-elements": "
|
|
21
|
+
"create-elements": "index.js"
|
|
22
22
|
},
|
|
23
|
-
"files": [
|
|
23
|
+
"files": [
|
|
24
|
+
"index.js",
|
|
25
|
+
"template/"
|
|
26
|
+
]
|
|
24
27
|
}
|
package/template/README.md
CHANGED
|
@@ -1,279 +1,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# elements-app
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
immutability, and HTML semantics.
|
|
3
|
+
A tiny Vite template using `@pfern/elements`.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
Includes:
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* No hooks, no classes, no virtual DOM heuristics
|
|
7
|
+
- Minimal client-side routing (history + `popstate`)
|
|
8
|
+
- A todos example (`src/components/todos.js`)
|
|
9
|
+
- Two independent counters (`src/components/counter.js`)
|
|
10
|
+
- Pico.css styling (`src/style.css`)
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## Why Elements.js?
|
|
17
|
-
|
|
18
|
-
Modern frameworks introduced declarative UI—but buried it beneath lifecycle
|
|
19
|
-
hooks, mutable state, and complex diffing algorithms.
|
|
20
|
-
|
|
21
|
-
**Elements.js goes further:**
|
|
22
|
-
|
|
23
|
-
* Pure functions represent both logic and view
|
|
24
|
-
* The DOM *is* your state model
|
|
25
|
-
* Re-rendering is *recursion*, not reconciliation
|
|
26
|
-
|
|
27
|
-
> Can UI be defined as a tree of pure function calls—nothing more?
|
|
28
|
-
|
|
29
|
-
Yes. Elements.js proves it.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Philosophy
|
|
34
|
-
|
|
35
|
-
### Declarative from top to bottom
|
|
36
|
-
|
|
37
|
-
* No internal component state
|
|
38
|
-
* No lifecycle methods or effects
|
|
39
|
-
* Every component is a function
|
|
40
|
-
|
|
41
|
-
To update a view: just **call the function again** with new arguments. The DOM
|
|
42
|
-
subtree is replaced in place.
|
|
43
|
-
|
|
44
|
-
### State lives in the DOM
|
|
45
|
-
|
|
46
|
-
There is no observer graph, no `useState`, and no memory of previous renders.
|
|
47
|
-
The DOM node *is the history*. Input state is passed as an argument.
|
|
48
|
-
|
|
49
|
-
### Minimal abstraction
|
|
50
|
-
|
|
51
|
-
* No keys, refs, proxies, or context systems
|
|
52
|
-
* No transpilation step
|
|
53
|
-
* No reactive graph to debug
|
|
54
|
-
|
|
55
|
-
Elements.js embraces the full truth of each function call as the only valid
|
|
56
|
-
state.
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Example: Counter
|
|
61
|
-
|
|
62
|
-
```js
|
|
63
|
-
import { button, component, div, output } from '@pfern/elements'
|
|
64
|
-
|
|
65
|
-
export const counter = component((count = 0) =>
|
|
66
|
-
div(
|
|
67
|
-
output(count),
|
|
68
|
-
button(
|
|
69
|
-
{ onclick: () => counter(count + 1) },
|
|
70
|
-
'Increment')))
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
* Each click returns a new call to `counter(count + 1)`
|
|
74
|
-
* The old DOM node is replaced with the new one
|
|
75
|
-
* No virtual DOM, no diffing
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## Form Example: Todos App
|
|
80
|
-
|
|
81
|
-
```js
|
|
82
|
-
|
|
83
|
-
import { button, component, div,
|
|
84
|
-
form, input, li, span, ul } from '@pfern/elements'
|
|
85
|
-
|
|
86
|
-
export const todos = component(
|
|
87
|
-
(items = [{ value: 'Add my first todo', done: true }]) => {
|
|
88
|
-
|
|
89
|
-
const add = ({ todo: { value } }) =>
|
|
90
|
-
value && todos([...items, { value, done: false }])
|
|
91
|
-
|
|
92
|
-
const remove = item =>
|
|
93
|
-
todos(items.filter(i => i !== item))
|
|
94
|
-
|
|
95
|
-
const toggle = item =>
|
|
96
|
-
todos(items.map(i => i === item ? { ...i, done: !item.done } : i))
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
div({ class: 'todos' },
|
|
100
|
-
|
|
101
|
-
form({ onsubmit: add },
|
|
102
|
-
input({ name: 'todo', placeholder: 'What needs doing?' }),
|
|
103
|
-
button({ type: 'submit' }, 'Add')),
|
|
104
|
-
|
|
105
|
-
ul(...items.map(item =>
|
|
106
|
-
li(
|
|
107
|
-
{ style:
|
|
108
|
-
{ 'text-decoration': item.done ? 'line-through' : 'none' } },
|
|
109
|
-
span({ onclick: () => toggle(item) }, item.value),
|
|
110
|
-
button({ onclick: () => remove(item) }, '✕'))))))})
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
This is a complete MVC-style app:
|
|
115
|
-
|
|
116
|
-
* Stateless
|
|
117
|
-
* Immutable
|
|
118
|
-
* Pure
|
|
119
|
-
|
|
120
|
-
You can view these examples live on [Github
|
|
121
|
-
Pages](https://pfernandez.github.io/elements/) or by running them locally with
|
|
122
|
-
`npm run dev`.
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Root Rendering Shortcut
|
|
127
|
-
|
|
128
|
-
If you use `html`, `head`, or `body` as the top-level tag, `render()` will
|
|
129
|
-
automatically mount into the corresponding document element—no need to pass a
|
|
130
|
-
container.
|
|
131
|
-
|
|
132
|
-
```js
|
|
133
|
-
import {
|
|
134
|
-
body, h1, h2, head, header, html,
|
|
135
|
-
link, main, meta, render, section, title
|
|
136
|
-
} from './elements.js'
|
|
137
|
-
import { todos } from './components/todos.js'
|
|
138
|
-
|
|
139
|
-
render(
|
|
140
|
-
html(
|
|
141
|
-
head(
|
|
142
|
-
title('Elements.js'),
|
|
143
|
-
meta({ name: 'viewport',
|
|
144
|
-
content: 'width=device-width, initial-scale=1.0' }),
|
|
145
|
-
link({ rel: 'stylesheet', href: 'css/style.css' })
|
|
146
|
-
),
|
|
147
|
-
body(
|
|
148
|
-
header(h1('Elements.js Demo')),
|
|
149
|
-
main(
|
|
150
|
-
section(
|
|
151
|
-
h2('Todos'),
|
|
152
|
-
todos())))))
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
## Declarative Events
|
|
158
|
-
|
|
159
|
-
All event listeners in Elements.js are pure functions. You can return a vnode
|
|
160
|
-
from a listener to declaratively update the component tree—- no mutation or
|
|
161
|
-
imperative logic required.
|
|
162
|
-
|
|
163
|
-
### General Behavior
|
|
164
|
-
|
|
165
|
-
* Any event handler (e.g. `onclick`, `onsubmit`, `oninput`) may return a new
|
|
166
|
-
vnode to trigger a subtree replacement.
|
|
167
|
-
* If the handler returns `undefined`, the event is treated as passive (no update
|
|
168
|
-
occurs).
|
|
169
|
-
* Returned vnodes are passed to `component()` to re-render declaratively.
|
|
170
|
-
|
|
171
|
-
### Form Events
|
|
172
|
-
|
|
173
|
-
For `onsubmit`, `oninput`, and `onchange`, Elements.js provides a special
|
|
174
|
-
signature:
|
|
175
|
-
|
|
176
|
-
```js
|
|
177
|
-
(event.target.elements, event)
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
That is, your handler receives:
|
|
181
|
-
|
|
182
|
-
1. `elements`: the HTML form’s named inputs
|
|
183
|
-
2. `event`: the original DOM event object
|
|
184
|
-
|
|
185
|
-
Elements.js will automatically call `event.preventDefault()` *only if* your
|
|
186
|
-
handler returns a vnode.
|
|
187
|
-
|
|
188
|
-
```js
|
|
189
|
-
form({
|
|
190
|
-
onsubmit: ({ todo: { value } }, e) =>
|
|
191
|
-
value && todos([...items, { value, done: false }])
|
|
192
|
-
})
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
If the handler returns nothing, `preventDefault()` is skipped and the form
|
|
196
|
-
submits natively.
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
## API
|
|
201
|
-
|
|
202
|
-
### `component(fn)`
|
|
203
|
-
|
|
204
|
-
Wrap a recursive pure function that returns a vnode.
|
|
205
|
-
|
|
206
|
-
### `render(vnode[, container])`
|
|
207
|
-
|
|
208
|
-
Render a vnode into the DOM. If `vnode[0]` is `html`, `head`, or `body`, no
|
|
209
|
-
`container` is required.
|
|
210
|
-
|
|
211
|
-
### DOM Elements
|
|
212
|
-
|
|
213
|
-
Every HTML and SVG tag is available as a function:
|
|
214
|
-
|
|
215
|
-
```js
|
|
216
|
-
div({ id: 'box' }, 'hello')
|
|
217
|
-
svg({ width: 100 }, circle({ r: 10 }))
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### TypeScript & JSDoc
|
|
221
|
-
|
|
222
|
-
Each tag function (e.g. `div`, `button`, `svg`) includes a `@typedef` and
|
|
223
|
-
MDN-sourced description to:
|
|
224
|
-
|
|
225
|
-
* Provide editor hints
|
|
226
|
-
* Encourage accessibility and semantic markup
|
|
227
|
-
* Enable intelligent autocomplete
|
|
228
|
-
|
|
229
|
-
### Testing Philosophy
|
|
230
|
-
|
|
231
|
-
Elements are data-in, data-out only, so mocking and headless browsers like
|
|
232
|
-
`jsdom` are unnecessary out of the box. See the tests [in this
|
|
233
|
-
repository](test/README.md) for some examples.
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
## Status
|
|
238
|
-
|
|
239
|
-
* 🧪 Fully tested (data-in/data-out behavior)
|
|
240
|
-
* ⚡ Under 2kB min+gzip
|
|
241
|
-
* ✅ Node and browser compatible
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## Installation
|
|
12
|
+
## Getting started
|
|
246
13
|
|
|
247
14
|
```bash
|
|
248
|
-
npm install
|
|
15
|
+
npm install
|
|
16
|
+
npm run dev
|
|
249
17
|
```
|
|
250
18
|
|
|
251
|
-
|
|
19
|
+
## Scripts
|
|
252
20
|
|
|
253
|
-
```
|
|
254
|
-
|
|
21
|
+
```bash
|
|
22
|
+
npm run dev
|
|
23
|
+
npm run build
|
|
24
|
+
npm run preview
|
|
25
|
+
npm test
|
|
255
26
|
```
|
|
256
27
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
## Summary
|
|
260
|
-
|
|
261
|
-
Elements.js is a thought experiment turned practical:
|
|
262
|
-
|
|
263
|
-
> Can UI be nothing but functions?
|
|
264
|
-
|
|
265
|
-
Turns out, yes.
|
|
266
|
-
|
|
267
|
-
* No diffing
|
|
268
|
-
* No state hooks
|
|
269
|
-
* No lifecycle
|
|
270
|
-
* No reconciliation heuristics
|
|
271
|
-
|
|
272
|
-
Just pure declarative HTML—rewritten in JavaScript.
|
|
273
|
-
|
|
274
|
-
---
|
|
275
|
-
|
|
276
|
-
**Lightweight. Immutable. Composable.**
|
|
277
|
-
|
|
278
|
-
Give it a try. You might never go back.
|
|
28
|
+
## Routing
|
|
279
29
|
|
|
30
|
+
Routes are defined in `src/router.js` and used from `src/components/app.js`.
|
|
31
|
+
Navigation uses the History API so the address bar stays in sync.
|
package/template/index.html
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<link rel="icon" href="data:x-icon">
|
|
7
|
+
<title>elements.js app</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<script type="module" src="src/index.js"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
package/template/package.json
CHANGED
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
"url": ""
|
|
17
17
|
},
|
|
18
18
|
"author": "",
|
|
19
|
-
"license": "
|
|
19
|
+
"license": "MIT",
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"eslint": "^9.39.2",
|
|
22
22
|
"vite": "^7.3.1"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@pfern/elements": "
|
|
25
|
+
"@pfern/elements": "latest",
|
|
26
26
|
"@picocss/pico": "^2.1.1"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { a, component, div, h2, main, nav, section } from '@pfern/elements'
|
|
2
|
+
import { counter } from './counter.js'
|
|
3
|
+
import { todos } from './todos.js'
|
|
4
|
+
import { currentPath, go, normalizePath } from '../router.js'
|
|
5
|
+
|
|
6
|
+
const link = (path, label, active) =>
|
|
7
|
+
a({
|
|
8
|
+
href: path,
|
|
9
|
+
class: active ? 'active' : '',
|
|
10
|
+
onclick: () => app(go(path, { force: true }))
|
|
11
|
+
}, label)
|
|
12
|
+
|
|
13
|
+
const navbar = path =>
|
|
14
|
+
nav(
|
|
15
|
+
link('/', 'Home', path === '/'),
|
|
16
|
+
link('/todos', 'Todos', path === '/todos'),
|
|
17
|
+
link('/counters', 'Counters', path === '/counters'))
|
|
18
|
+
|
|
19
|
+
const home = () =>
|
|
20
|
+
section(
|
|
21
|
+
h2('Home'),
|
|
22
|
+
div(`This template shows a tiny router (history + popstate), a todos app,
|
|
23
|
+
and independent counters.`))
|
|
24
|
+
|
|
25
|
+
const counters = () =>
|
|
26
|
+
section({ class: 'grid' },
|
|
27
|
+
div(
|
|
28
|
+
h2('Counter 1'),
|
|
29
|
+
counter()),
|
|
30
|
+
div(
|
|
31
|
+
h2('Counter 2'),
|
|
32
|
+
counter()))
|
|
33
|
+
|
|
34
|
+
export const app = component((path = currentPath()) => {
|
|
35
|
+
path = normalizePath(path)
|
|
36
|
+
|
|
37
|
+
const view =
|
|
38
|
+
path === '/todos' ? todos()
|
|
39
|
+
: path === '/counters' ? counters()
|
|
40
|
+
: home()
|
|
41
|
+
|
|
42
|
+
return main(
|
|
43
|
+
navbar(path),
|
|
44
|
+
view)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
if (typeof window !== 'undefined') {
|
|
48
|
+
window.addEventListener('popstate', () => app(currentPath()))
|
|
49
|
+
}
|
|
50
|
+
|
|
@@ -3,7 +3,6 @@ import { button, component, div, output } from '@pfern/elements'
|
|
|
3
3
|
export const counter = component((count = 0) =>
|
|
4
4
|
div(
|
|
5
5
|
output(count),
|
|
6
|
-
button(
|
|
7
|
-
|
|
8
|
-
'Increment')))
|
|
6
|
+
button({ onclick: () => counter(count + 1) },
|
|
7
|
+
'Increment')))
|
|
9
8
|
|
|
@@ -21,8 +21,7 @@ export const todos = component(
|
|
|
21
21
|
button({ type: 'submit' }, 'Add')),
|
|
22
22
|
|
|
23
23
|
ul(...items.map(item =>
|
|
24
|
-
li(
|
|
25
|
-
{ style:
|
|
24
|
+
li({ style:
|
|
26
25
|
{ 'text-decoration': item.done ? 'line-through' : 'none' } },
|
|
27
26
|
span({ onclick: () => toggle(item) }, item.value),
|
|
28
27
|
button({ onclick: () => remove(item) }, '✕'))))))})
|
package/template/src/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import './style.css'
|
|
2
|
-
import { body,
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import { todos } from './components/todos.js'
|
|
2
|
+
import { body, h1, head, header, html,
|
|
3
|
+
meta, render, title } from '@pfern/elements'
|
|
4
|
+
import { app } from './components/app.js'
|
|
6
5
|
|
|
7
6
|
render(
|
|
8
7
|
html(
|
|
@@ -13,15 +12,5 @@ render(
|
|
|
13
12
|
body(
|
|
14
13
|
header(
|
|
15
14
|
h1('Elements.js Demo')),
|
|
16
|
-
|
|
17
|
-
section(
|
|
18
|
-
h2('Todos'),
|
|
19
|
-
todos()),
|
|
20
|
-
section({ class: 'grid' },
|
|
21
|
-
div(
|
|
22
|
-
h2('Counter 1'),
|
|
23
|
-
counter()),
|
|
24
|
-
div(
|
|
25
|
-
h2('Counter 2'),
|
|
26
|
-
counter()))))))
|
|
15
|
+
app())))
|
|
27
16
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const routes = [
|
|
2
|
+
'/',
|
|
3
|
+
'/todos',
|
|
4
|
+
'/counters',
|
|
5
|
+
]
|
|
6
|
+
|
|
7
|
+
export const navigate = (path, { replace = false, force = false } = {}) => {
|
|
8
|
+
if (typeof window === 'undefined') return
|
|
9
|
+
|
|
10
|
+
const url = new URL(path, window.location.origin)
|
|
11
|
+
const isSame =
|
|
12
|
+
url.pathname === window.location.pathname
|
|
13
|
+
&& url.search === window.location.search
|
|
14
|
+
&& url.hash === window.location.hash
|
|
15
|
+
|
|
16
|
+
if (!force && isSame) return
|
|
17
|
+
|
|
18
|
+
const fn = replace ? 'replaceState' : 'pushState'
|
|
19
|
+
window.history[fn]({}, '', url)
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
window.dispatchEvent(new PopStateEvent('popstate'))
|
|
23
|
+
} catch {
|
|
24
|
+
window.dispatchEvent(new Event('popstate'))
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const currentPath = () =>
|
|
29
|
+
(typeof window === 'undefined')
|
|
30
|
+
? '/'
|
|
31
|
+
: window.location.pathname || '/'
|
|
32
|
+
|
|
33
|
+
export const normalizePath = (path = '/') =>
|
|
34
|
+
routes.includes(path) ? path : '/'
|
|
35
|
+
|
|
36
|
+
export const go = (path, { force = false } = {}) => {
|
|
37
|
+
const next = normalizePath(path)
|
|
38
|
+
navigate(next, { force })
|
|
39
|
+
return next
|
|
40
|
+
}
|
package/template/src/style.css
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
|
|
2
1
|
/*******************************************************************************
|
|
3
2
|
* Reset & base theme
|
|
4
3
|
******************************************************************************/
|
|
5
4
|
|
|
6
|
-
@import '@picocss/pico/css/pico.
|
|
7
|
-
|
|
8
|
-
:root {
|
|
9
|
-
--pico-font-family-sans-serif: 'Arial', sans-serif;
|
|
10
|
-
--pico-border-radius: 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/* Light color scheme (Default) */
|
|
14
|
-
/* Can be forced with data-theme="light" */
|
|
15
|
-
[data-theme="light"],
|
|
16
|
-
:root:not([data-theme="dark"]) {
|
|
17
|
-
--pico-primary-background: #ccc;
|
|
18
|
-
--pico-primary-border: #ccc;
|
|
19
|
-
--pico-primary-hover-color: var(--pico-primary-inverse);
|
|
20
|
-
--pico-primary-hover-background: transparent;
|
|
21
|
-
--pico-primary-hover-border: var(--pico-primary-hover-color);
|
|
22
|
-
--pico-button-hover-box-shadow: var(--pico-primary-hover-background);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
input:not([type=checkbox],[type=radio],[type=range]) {
|
|
26
|
-
height: auto;
|
|
27
|
-
}
|
|
5
|
+
@import '@picocss/pico/css/pico.grey.css';
|
|
28
6
|
|
|
29
7
|
/*******************************************************************************
|
|
30
8
|
* Layout
|
|
@@ -43,6 +21,16 @@ body>header, header {
|
|
|
43
21
|
padding-bottom: 0;
|
|
44
22
|
}
|
|
45
23
|
|
|
24
|
+
nav {
|
|
25
|
+
display: inline-flex;
|
|
26
|
+
gap: 1em;
|
|
27
|
+
padding-bottom: 1em;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
nav a.active {
|
|
31
|
+
text-decoration: underline;
|
|
32
|
+
}
|
|
33
|
+
|
|
46
34
|
section {
|
|
47
35
|
border-top: 1px solid #ddd;
|
|
48
36
|
padding-top: 1em;
|
|
@@ -78,15 +66,16 @@ li > button {
|
|
|
78
66
|
******************************************************************************/
|
|
79
67
|
|
|
80
68
|
.todos form {
|
|
81
|
-
max-width:
|
|
69
|
+
max-width: 512px;
|
|
82
70
|
}
|
|
83
71
|
|
|
84
72
|
.todos form input {
|
|
85
|
-
width:
|
|
73
|
+
width: 78%;
|
|
86
74
|
}
|
|
87
75
|
|
|
88
76
|
.todos form button {
|
|
89
|
-
width:
|
|
77
|
+
width: 20%;
|
|
78
|
+
margin-left: 2%;
|
|
90
79
|
}
|
|
91
80
|
|
|
92
81
|
.todos li {
|