@cardano-slips/flow 0.2.0 → 0.3.1
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 +53 -9
- package/dist/address.d.ts +12 -0
- package/dist/address.d.ts.map +1 -1
- package/dist/address.js +43 -0
- package/dist/address.js.map +1 -1
- package/dist/card.css +492 -0
- package/dist/card.d.ts +41 -0
- package/dist/card.d.ts.map +1 -0
- package/dist/card.js +69 -0
- package/dist/card.js.map +1 -0
- package/dist/complete-error.d.ts +8 -1
- package/dist/complete-error.d.ts.map +1 -1
- package/dist/complete-error.js +6 -1
- package/dist/complete-error.js.map +1 -1
- package/dist/complete.d.ts +29 -10
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +83 -9
- package/dist/complete.js.map +1 -1
- package/dist/derived.d.ts +8 -0
- package/dist/derived.d.ts.map +1 -0
- package/dist/derived.js +2 -0
- package/dist/derived.js.map +1 -0
- package/dist/effects-panel.d.ts +37 -0
- package/dist/effects-panel.d.ts.map +1 -0
- package/dist/effects-panel.js +64 -0
- package/dist/effects-panel.js.map +1 -0
- package/dist/effects.css +629 -0
- package/dist/exchange.d.ts +54 -0
- package/dist/exchange.d.ts.map +1 -0
- package/dist/exchange.js +136 -0
- package/dist/exchange.js.map +1 -0
- package/dist/explain.d.ts +39 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +172 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts +19 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +74 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +402 -0
- package/dist/ledger.js.map +1 -0
- package/dist/outcome-panel.d.ts +63 -0
- package/dist/outcome-panel.d.ts.map +1 -0
- package/dist/outcome-panel.js +40 -0
- package/dist/outcome-panel.js.map +1 -0
- package/dist/parameter-form.d.ts +26 -0
- package/dist/parameter-form.d.ts.map +1 -0
- package/dist/parameter-form.js +42 -0
- package/dist/parameter-form.js.map +1 -0
- package/dist/slip-card.d.ts +55 -0
- package/dist/slip-card.d.ts.map +1 -0
- package/dist/slip-card.js +89 -0
- package/dist/slip-card.js.map +1 -0
- package/dist/tokens.css +57 -32
- package/package.json +11 -7
- package/src/address.ts +53 -0
- package/src/card.css +492 -0
- package/src/card.ts +86 -0
- package/src/complete-error.ts +14 -2
- package/src/complete.ts +128 -19
- package/src/derived.ts +8 -0
- package/src/effects-panel.tsx +386 -0
- package/src/effects.css +629 -0
- package/src/exchange.ts +212 -0
- package/src/explain.ts +211 -0
- package/src/index.ts +39 -3
- package/src/ledger.ts +503 -0
- package/src/outcome-panel.tsx +263 -0
- package/src/parameter-form.tsx +138 -0
- package/src/slip-card.tsx +244 -0
- package/src/tokens.css +57 -32
package/README.md
CHANGED
|
@@ -30,6 +30,51 @@ These components are meant to be dropped into someone else's page. No fixed posi
|
|
|
30
30
|
|
|
31
31
|
React is a **peer** dependency: your copy is the one that renders, and a second copy in the tree is the oldest breakage in the ecosystem.
|
|
32
32
|
|
|
33
|
+
## Components
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { SlipCard } from "@cardano-slips/flow"
|
|
37
|
+
import "@cardano-slips/flow/tokens.css"
|
|
38
|
+
import "@cardano-slips/flow/card.css"
|
|
39
|
+
|
|
40
|
+
<SlipCard slip={slip} discoveryUrl={url} onSubmit={({ href, values }) => …} />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`SlipCard` renders what the endpoint declared — icon, title, description, and a button per linked action — generates the form its parameters describe, and hands you the `POST` target once every value passes. `SlipCardSkeleton` holds the same box while the metadata is in flight; `SlipCardError` replaces it when the endpoint never answered.
|
|
44
|
+
|
|
45
|
+
The card makes no claim about the transaction. Title and description are the publisher's words, and checking them against what the transaction actually does is the effects panel's job — against the bytes, not against this card.
|
|
46
|
+
|
|
47
|
+
Nothing here decides for a person on the endpoint's behalf: a value that fails `required`, `min` or `max` is caught before anything is sent, in `core`'s own sentence, so the same bad value reads identically in every client. A field the endpoint itself refuses lands on that field and leaves the card standing, and what was typed stays typed.
|
|
48
|
+
|
|
49
|
+
### Restyling
|
|
50
|
+
|
|
51
|
+
`card.css` is optional. Import it and you get the design sheet; skip it and you get the markup, where every element carries a class of ours and nothing else:
|
|
52
|
+
|
|
53
|
+
| Class | What it is |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| `slip-card` | the card, and `slip-card--loading` / `slip-card--failed` for the other two; `data-closed` marks a Slip that cannot be signed and `data-busy` one with a request in flight |
|
|
56
|
+
| `slip-card__head`, `__icon`, `__title`, `__origin`, `__description` | what the endpoint declared |
|
|
57
|
+
| `slip-card__actions`, `__action` | the buttons; `data-primary` marks the one that is emphasised |
|
|
58
|
+
| `slip-card__reason`, `__note` | why an action is closed, and the promise the card makes |
|
|
59
|
+
| `slip-fields`, `slip-field`, `slip-field__*` | the generated form; `data-invalid` marks a field that was rejected |
|
|
60
|
+
|
|
61
|
+
The card is the publisher's surface and is meant to be restyled. The chrome that judges it — the effects panel, the mismatch block, the network indicator — is not, hosted or self-hosted: a publisher who can restyle the surface that judges them makes the verdict look like something they control.
|
|
62
|
+
|
|
63
|
+
### The effects panel
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { EffectsPanel } from "@cardano-slips/flow"
|
|
67
|
+
import "@cardano-slips/flow/effects.css"
|
|
68
|
+
|
|
69
|
+
<EffectsPanel claim={action.label} derived={attempt} verdict={{ _tag: "match" }} onSign={…} onCancel={…} />
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`EffectsPanel` shows what a transaction does, read from its own bytes: the figure the person is deciding on, then one row per effect — payments, tokens received, withdrawals, certificates, the fee, and deposits and refunds, each marked refundable where the ledger guarantees it and as stated where only the transaction claims it. `derived` is what `completeIntent` hands `onAttempt`, or what a `Blocked` refusal carries.
|
|
73
|
+
|
|
74
|
+
`verdict` is the verifier's own and is required. On a mismatch the sign button is not rendered at all — the claim is set beside what the transaction does, the one row the link never described is marked, and the only ways out are closing it and reporting the link. There is no prop that brings the button back. The countdown takes the headline's place under a minute, and an expired transaction offers `onRebuild` instead of a signature.
|
|
75
|
+
|
|
76
|
+
`effects.css` is the fixed chrome above: import it as it is.
|
|
77
|
+
|
|
33
78
|
## Tokens
|
|
34
79
|
|
|
35
80
|
```ts
|
|
@@ -38,24 +83,23 @@ import "@cardano-slips/flow/tokens.css"
|
|
|
38
83
|
|
|
39
84
|
```html
|
|
40
85
|
<div class="slip-root">…</div>
|
|
41
|
-
<div class="slip-root" data-theme="dark">…</div>
|
|
42
86
|
```
|
|
43
87
|
|
|
44
|
-
The tokens are scoped to `slip-root` rather than `:root`, because a package that writes `--ink` onto the document redefines whatever the host already called that.
|
|
88
|
+
The tokens are scoped to `slip-root` rather than `:root`, because a package that writes `--ink` onto the document redefines whatever the host already called that. Every component of ours puts the class on its own root, so an embedded card carries its tokens with it. There is no reset in the file and no rule that selects an element, so an inherited font stack stays where it is until something of ours asks for a token.
|
|
45
89
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Three families of surface, and the difference is the point:
|
|
90
|
+
There is one theme. The design sheet collapsed an earlier light/dark pair into a single ground that leans light, so there is no `data-theme` and no role that means two things — a component that can ask which theme it is in is a component that will answer differently from the next one. What carries the weight instead is that the three families of surface are genuinely different surfaces:
|
|
49
91
|
|
|
50
92
|
| Family | What it is |
|
|
51
93
|
| --- | --- |
|
|
52
94
|
| `--page`, `--card` | the ordinary ground, and what lifts off it |
|
|
53
|
-
| `--vault-*` | the transaction preview,
|
|
54
|
-
| `--dark-*` | code blocks and the Open Graph card
|
|
95
|
+
| `--vault-*` | the transaction preview, on slate — the one grave surface, and the reason the rest can stay bright |
|
|
96
|
+
| `--dark-*` | code blocks and the Open Graph card, fixed wherever they appear because neither sits on a ground we control |
|
|
97
|
+
|
|
98
|
+
There is one typeface, Poppins, and one token naming it. The package does not fetch it — a stylesheet that reaches a third-party CDN makes a privacy and performance decision that belongs to whoever owns the page — so load Poppins yourself, or take the `system-ui` the stack falls back to. Type arrives as whole roles — `font: var(--type-card-title)` carries family, weight, size and line-height together — because a role is a pairing, not a size, and because a component that can apply the size without the family is a component that will. What separates a title from a label is weight and size; there is no second face to reach for, and `--type-technical` is the role for text meant for a machine — an address, a hash, an error code.
|
|
55
99
|
|
|
56
|
-
|
|
100
|
+
Depth is a border; there is no shadow token. Buttons are outlined rather than filled: the accent is the label and the edge, not the ground.
|
|
57
101
|
|
|
58
|
-
|
|
102
|
+
Three rules the tests keep: no colour is written anywhere but `tokens.css`, every `var(--…)` a stylesheet reaches for is a token that exists, and the design sheet's contrast audit is recomputed against these values rather than trusted — which is how `--vault-bad` came to be a shade lighter here than on the sheet, where the value drawn measures 4.42:1 on the preview surface under a row labelled a pass. `--accent-fill` is a fill — it does not clear AA on a light card, which is why `--accent-text` exists for any accent-coloured word, and it stays a fill on the preview surface where the ratio alone would allow otherwise.
|
|
59
103
|
|
|
60
104
|
## Entry point
|
|
61
105
|
|
package/dist/address.d.ts
CHANGED
|
@@ -10,4 +10,16 @@ export type WalletAddress = {
|
|
|
10
10
|
* browser extension chose to send, and every refusal reaches a person as words.
|
|
11
11
|
*/
|
|
12
12
|
export declare const readWalletAddress: (hex: string) => Either.Either<WalletAddress, string>;
|
|
13
|
+
export type OwnAddress = {
|
|
14
|
+
/** Raw, as the verifier compares them against the body's addresses. */
|
|
15
|
+
readonly bytes: Uint8Array;
|
|
16
|
+
/** Absent for a Byron address, whose header carries no network. */
|
|
17
|
+
readonly networkId?: NetworkId;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Reads an address the wallet calls its own: any payment address, a reward
|
|
21
|
+
* account, or a Byron address an old wallet still reports as used. Refusing
|
|
22
|
+
* the Byron one would stop the whole flow over an address nothing here spends.
|
|
23
|
+
*/
|
|
24
|
+
export declare const readOwnAddress: (hex: unknown) => Either.Either<OwnAddress, string>;
|
|
13
25
|
//# sourceMappingURL=address.d.ts.map
|
package/dist/address.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAEvC,sEAAsE;AACtE,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,CAAA;AAE7B,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;CAC9B,CAAA;AA0BD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAqBlF,CAAA"}
|
|
1
|
+
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAEvC,sEAAsE;AACtE,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,CAAA;AAE7B,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;CAC9B,CAAA;AA0BD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAqBlF,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;CAC/B,CAAA;AAkBD;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,OAAO,KAAG,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAuB7E,CAAA"}
|
package/dist/address.js
CHANGED
|
@@ -51,4 +51,47 @@ export const readWalletAddress = (hex) => {
|
|
|
51
51
|
onRight: () => Either.right({ bech32, networkId: networkId })
|
|
52
52
|
});
|
|
53
53
|
};
|
|
54
|
+
const BYRON_TYPE = 8;
|
|
55
|
+
const FIRST_REWARD_TYPE = 14;
|
|
56
|
+
const LAST_REWARD_TYPE = 15;
|
|
57
|
+
/** A header byte and one or two 28-byte hashes. */
|
|
58
|
+
const shelleyLengths = {
|
|
59
|
+
0: [57],
|
|
60
|
+
1: [57],
|
|
61
|
+
2: [57],
|
|
62
|
+
3: [57],
|
|
63
|
+
6: [29],
|
|
64
|
+
7: [29],
|
|
65
|
+
14: [29],
|
|
66
|
+
15: [29]
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Reads an address the wallet calls its own: any payment address, a reward
|
|
70
|
+
* account, or a Byron address an old wallet still reports as used. Refusing
|
|
71
|
+
* the Byron one would stop the whole flow over an address nothing here spends.
|
|
72
|
+
*/
|
|
73
|
+
export const readOwnAddress = (hex) => {
|
|
74
|
+
// Typed as hex by CIP-30, and sent by an extension under no obligation to.
|
|
75
|
+
const raw = typeof hex === "string" ? fromHex(hex.trim()) : undefined;
|
|
76
|
+
if (raw === undefined)
|
|
77
|
+
return refuse(`${String(hex).slice(0, 16)}… is not hex`);
|
|
78
|
+
const bytes = unwrapCbor(raw);
|
|
79
|
+
const header = bytes[0];
|
|
80
|
+
const type = header >> 4;
|
|
81
|
+
if (type === BYRON_TYPE)
|
|
82
|
+
return Either.right({ bytes });
|
|
83
|
+
const networkId = header & 0x0f;
|
|
84
|
+
if (networkId !== 0 && networkId !== 1)
|
|
85
|
+
return refuse(`the address header names network ${networkId}`);
|
|
86
|
+
const payment = type <= LAST_PAYMENT_TYPE;
|
|
87
|
+
const reward = type >= FIRST_REWARD_TYPE && type <= LAST_REWARD_TYPE;
|
|
88
|
+
if (!payment && !reward)
|
|
89
|
+
return refuse(`address type ${type} is not one a wallet holds`);
|
|
90
|
+
// Pointer addresses (4, 5) end in variable-length integers, so only a floor applies.
|
|
91
|
+
const lengths = shelleyLengths[type];
|
|
92
|
+
const fits = lengths === undefined ? bytes.length > 29 : lengths.includes(bytes.length);
|
|
93
|
+
if (!fits)
|
|
94
|
+
return refuse(`${bytes.length} bytes is the wrong length for address type ${type}`);
|
|
95
|
+
return Either.right({ bytes, networkId: networkId });
|
|
96
|
+
};
|
|
54
97
|
//# sourceMappingURL=address.js.map
|
package/dist/address.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"address.js","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"address.js","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"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"}
|
package/dist/card.css
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The action card, drawn from `2 · Action card` on the design sheet. Optional:
|
|
3
|
+
* a consumer who imports only `tokens.css` gets the markup and the class names
|
|
4
|
+
* and styles them however they like. Nothing here writes a colour — every value
|
|
5
|
+
* is a token, and `test/no-hard-coded-colours.test.ts` fails if that stops
|
|
6
|
+
* being true.
|
|
7
|
+
*
|
|
8
|
+
* The card is the publisher's surface (`13 · Chrome rules`): its content is
|
|
9
|
+
* their claim, so its look is theirs to own. The chrome that judges that claim
|
|
10
|
+
* — the effects panel and the mismatch block — is fixed and is not styled here.
|
|
11
|
+
*
|
|
12
|
+
* Every text rule states family, size, weight and line-height together through
|
|
13
|
+
* a `--type-*` role, so a host page's inherited stack cannot reach in.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
.slip-card {
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
position: relative;
|
|
19
|
+
|
|
20
|
+
/* Widths are answered against the card's own box: it never assumes it owns the viewport. */
|
|
21
|
+
container-type: inline-size;
|
|
22
|
+
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
gap: var(--space-16);
|
|
26
|
+
|
|
27
|
+
width: 100%;
|
|
28
|
+
max-width: 680px;
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: var(--space-20);
|
|
31
|
+
|
|
32
|
+
background: var(--card);
|
|
33
|
+
border: 1px solid var(--border);
|
|
34
|
+
border-radius: var(--radius-card);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.slip-card *,
|
|
38
|
+
.slip-card *::before,
|
|
39
|
+
.slip-card *::after {
|
|
40
|
+
box-sizing: border-box;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.slip-card__head {
|
|
44
|
+
display: flex;
|
|
45
|
+
gap: var(--space-12);
|
|
46
|
+
align-items: flex-start;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.slip-card__icon {
|
|
50
|
+
flex: none;
|
|
51
|
+
width: 48px;
|
|
52
|
+
height: 48px;
|
|
53
|
+
border: 1px solid var(--border);
|
|
54
|
+
border-radius: var(--radius-control);
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.slip-card__image,
|
|
59
|
+
.slip-card__pattern,
|
|
60
|
+
.slip-card__monogram {
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
width: 100%;
|
|
65
|
+
height: 100%;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.slip-card__image {
|
|
69
|
+
object-fit: cover;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* The two fallbacks. Neither is a generic glyph: one is the publisher's initial, the other is no claim at all. */
|
|
73
|
+
.slip-card__monogram {
|
|
74
|
+
background: var(--subtle);
|
|
75
|
+
color: var(--accent-text);
|
|
76
|
+
font: var(--type-card-title);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.slip-card__pattern {
|
|
80
|
+
background-image: repeating-linear-gradient(135deg, var(--stripe-a) 0 5px, var(--stripe-b) 5px 10px);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.slip-card__alarm {
|
|
84
|
+
display: flex;
|
|
85
|
+
flex: none;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
width: 48px;
|
|
89
|
+
height: 48px;
|
|
90
|
+
border: 1px solid var(--bad-line);
|
|
91
|
+
border-radius: var(--radius-control);
|
|
92
|
+
background: var(--bad-tint);
|
|
93
|
+
color: var(--bad);
|
|
94
|
+
font: var(--type-card-title);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.slip-card__identity {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
gap: var(--space-4);
|
|
101
|
+
min-width: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.slip-card__title {
|
|
105
|
+
margin: 0;
|
|
106
|
+
color: var(--ink);
|
|
107
|
+
font: var(--type-card-title);
|
|
108
|
+
text-wrap: pretty;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.slip-card__origin {
|
|
112
|
+
color: var(--muted);
|
|
113
|
+
font: var(--type-technical);
|
|
114
|
+
overflow-wrap: anywhere;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.slip-card__description {
|
|
118
|
+
margin: 0;
|
|
119
|
+
max-width: 52ch;
|
|
120
|
+
color: var(--muted);
|
|
121
|
+
font: var(--type-body);
|
|
122
|
+
text-wrap: pretty;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.slip-card__note {
|
|
126
|
+
margin: 0;
|
|
127
|
+
padding-top: var(--space-12);
|
|
128
|
+
border-top: 1px solid var(--divider);
|
|
129
|
+
color: var(--muted);
|
|
130
|
+
font: var(--type-label);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.slip-card__reason {
|
|
134
|
+
margin: 0;
|
|
135
|
+
color: var(--muted);
|
|
136
|
+
font: var(--type-label);
|
|
137
|
+
text-wrap: pretty;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* A closed card stays readable. Only the action is gone. */
|
|
141
|
+
.slip-card[data-closed] .slip-card__title {
|
|
142
|
+
color: var(--muted);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.slip-card[data-closed] .slip-card__pattern {
|
|
146
|
+
background-image: repeating-linear-gradient(135deg, var(--stripe-off-a) 0 5px, var(--stripe-off-b) 5px 10px);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.slip-card__actions {
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-wrap: wrap;
|
|
152
|
+
gap: var(--space-8);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/*
|
|
156
|
+
* One button, outlined. The sheet drew a filled primary until the palette
|
|
157
|
+
* change and now draws every action the same way, so `data-primary` marks which
|
|
158
|
+
* one leads without claiming a look the sheet no longer gives it.
|
|
159
|
+
*/
|
|
160
|
+
.slip-card__action {
|
|
161
|
+
flex: 0 1 auto;
|
|
162
|
+
height: 44px;
|
|
163
|
+
padding: 0 var(--space-20);
|
|
164
|
+
border: 1px solid var(--accent-action);
|
|
165
|
+
border-radius: var(--radius-control);
|
|
166
|
+
background: transparent;
|
|
167
|
+
color: var(--accent-action);
|
|
168
|
+
font: var(--type-button);
|
|
169
|
+
letter-spacing: var(--type-button-tracking);
|
|
170
|
+
white-space: nowrap;
|
|
171
|
+
cursor: pointer;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Alone, a button is the width of the card; among others it is the width of its words. */
|
|
175
|
+
.slip-card__action:only-child {
|
|
176
|
+
flex: 1 1 100%;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.slip-card__action:not(:disabled):hover {
|
|
180
|
+
border-color: var(--accent-hover);
|
|
181
|
+
background: var(--subtle);
|
|
182
|
+
color: var(--accent-hover);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.slip-card__action:disabled {
|
|
186
|
+
border-color: var(--border);
|
|
187
|
+
background: var(--subtle);
|
|
188
|
+
color: var(--muted);
|
|
189
|
+
cursor: not-allowed;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.slip-fields {
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
gap: var(--space-16);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.slip-field {
|
|
199
|
+
display: flex;
|
|
200
|
+
flex-direction: column;
|
|
201
|
+
gap: var(--space-8);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.slip-field__label {
|
|
205
|
+
color: var(--muted);
|
|
206
|
+
font: var(--type-label);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.slip-field__control {
|
|
210
|
+
display: flex;
|
|
211
|
+
align-items: center;
|
|
212
|
+
gap: var(--space-8);
|
|
213
|
+
height: 44px;
|
|
214
|
+
padding: 0 var(--space-12);
|
|
215
|
+
border: 1px solid var(--border);
|
|
216
|
+
border-radius: var(--radius-control);
|
|
217
|
+
background: var(--card);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.slip-field__control:focus-within {
|
|
221
|
+
border-color: var(--accent-fill);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.slip-field[data-invalid] .slip-field__control {
|
|
225
|
+
border-color: var(--bad-line);
|
|
226
|
+
background: var(--bad-tint);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.slip-field__input {
|
|
230
|
+
flex: 1;
|
|
231
|
+
min-width: 0;
|
|
232
|
+
padding: 0;
|
|
233
|
+
border: none;
|
|
234
|
+
outline: none;
|
|
235
|
+
background: transparent;
|
|
236
|
+
color: var(--ink);
|
|
237
|
+
font: var(--type-body);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* A figure is always Poppins 700 — the one type rule the token file states out loud. */
|
|
241
|
+
.slip-field__input--numeral {
|
|
242
|
+
font: var(--type-numeral);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* The helper line: what will be accepted on the left, whether it is required on the right. */
|
|
246
|
+
.slip-field__help {
|
|
247
|
+
display: flex;
|
|
248
|
+
justify-content: space-between;
|
|
249
|
+
gap: var(--space-12);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.slip-field__bounds,
|
|
253
|
+
.slip-field__note {
|
|
254
|
+
color: var(--muted);
|
|
255
|
+
font: var(--type-label);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* The error replaces the bounds rather than joining them: two lines of guidance is one too many. */
|
|
259
|
+
.slip-field[data-invalid] .slip-field__bounds {
|
|
260
|
+
color: var(--bad);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.slip-field__code {
|
|
264
|
+
color: var(--muted);
|
|
265
|
+
font: var(--type-technical);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.slip-card__action:focus-visible,
|
|
269
|
+
.slip-field__input:focus-visible {
|
|
270
|
+
outline: 2px solid var(--focus);
|
|
271
|
+
outline-offset: 2px;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.slip-card--loading {
|
|
275
|
+
animation: slip-pulse 1.6s ease-in-out infinite;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.slip-skeleton__lines {
|
|
279
|
+
display: flex;
|
|
280
|
+
flex-direction: column;
|
|
281
|
+
gap: var(--space-8);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.slip-skeleton {
|
|
285
|
+
display: block;
|
|
286
|
+
border-radius: var(--radius-chip);
|
|
287
|
+
background: var(--skeleton);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.slip-skeleton--icon {
|
|
291
|
+
flex: none;
|
|
292
|
+
width: 48px;
|
|
293
|
+
height: 48px;
|
|
294
|
+
border-radius: var(--radius-control);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.slip-skeleton--title {
|
|
298
|
+
width: 72%;
|
|
299
|
+
height: 18px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.slip-skeleton--origin {
|
|
303
|
+
width: 32%;
|
|
304
|
+
height: 12px;
|
|
305
|
+
background: var(--subtle);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.slip-skeleton--line {
|
|
309
|
+
width: 100%;
|
|
310
|
+
height: 12px;
|
|
311
|
+
background: var(--subtle);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.slip-skeleton--short {
|
|
315
|
+
width: 64%;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.slip-skeleton--action {
|
|
319
|
+
height: 44px;
|
|
320
|
+
border-radius: var(--radius-control);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* The receipt is back on the card surface: the preview's job is done, so its ground goes with it. */
|
|
324
|
+
.slip-receipt {
|
|
325
|
+
width: 100%;
|
|
326
|
+
max-width: 560px;
|
|
327
|
+
box-sizing: border-box;
|
|
328
|
+
padding: 24px;
|
|
329
|
+
display: flex;
|
|
330
|
+
flex-direction: column;
|
|
331
|
+
gap: var(--space-16);
|
|
332
|
+
color: var(--ink);
|
|
333
|
+
background: var(--card);
|
|
334
|
+
border: 1px solid var(--border);
|
|
335
|
+
border-radius: var(--radius-card);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.slip-receipt__head {
|
|
339
|
+
display: flex;
|
|
340
|
+
gap: 14px;
|
|
341
|
+
align-items: flex-start;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.slip-receipt__tick {
|
|
345
|
+
width: 48px;
|
|
346
|
+
height: 48px;
|
|
347
|
+
flex: none;
|
|
348
|
+
display: flex;
|
|
349
|
+
align-items: center;
|
|
350
|
+
justify-content: center;
|
|
351
|
+
font: var(--type-preview-title);
|
|
352
|
+
color: var(--pos);
|
|
353
|
+
background: var(--subtle);
|
|
354
|
+
border: 1px solid var(--border);
|
|
355
|
+
border-radius: var(--radius-control);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.slip-receipt__identity {
|
|
359
|
+
display: flex;
|
|
360
|
+
flex-direction: column;
|
|
361
|
+
gap: 6px;
|
|
362
|
+
min-width: 0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.slip-receipt__title {
|
|
366
|
+
margin: 0;
|
|
367
|
+
font: var(--type-card-title);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.slip-receipt__note {
|
|
371
|
+
font: var(--type-note);
|
|
372
|
+
color: var(--muted);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.slip-receipt__ledger {
|
|
376
|
+
padding: 0 14px;
|
|
377
|
+
border: 1px solid var(--border);
|
|
378
|
+
border-radius: var(--radius-control);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.slip-receipt__row {
|
|
382
|
+
display: flex;
|
|
383
|
+
justify-content: space-between;
|
|
384
|
+
align-items: baseline;
|
|
385
|
+
gap: var(--space-16);
|
|
386
|
+
padding: 12px 0;
|
|
387
|
+
font: var(--type-row);
|
|
388
|
+
color: var(--muted);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.slip-receipt__row + .slip-receipt__row {
|
|
392
|
+
border-top: 1px solid var(--divider);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.slip-receipt__amounts {
|
|
396
|
+
display: flex;
|
|
397
|
+
flex-direction: column;
|
|
398
|
+
align-items: flex-end;
|
|
399
|
+
font: var(--type-countdown);
|
|
400
|
+
color: var(--ink);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.slip-receipt__amount[data-tone="gained"] {
|
|
404
|
+
color: var(--pos);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.slip-receipt__amount[data-tone="fee"],
|
|
408
|
+
.slip-receipt__amount[data-tone="held"] {
|
|
409
|
+
color: var(--warn);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.slip-receipt__transaction {
|
|
413
|
+
display: flex;
|
|
414
|
+
flex-wrap: wrap;
|
|
415
|
+
align-items: center;
|
|
416
|
+
gap: var(--space-8) var(--space-12);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.slip-receipt__kicker {
|
|
420
|
+
flex-basis: 100%;
|
|
421
|
+
font: var(--type-kicker);
|
|
422
|
+
letter-spacing: var(--type-kicker-tracking);
|
|
423
|
+
text-transform: uppercase;
|
|
424
|
+
color: var(--muted);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.slip-receipt__id {
|
|
428
|
+
font: var(--type-technical);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.slip-receipt__link {
|
|
432
|
+
font: var(--type-technical);
|
|
433
|
+
color: var(--accent-text);
|
|
434
|
+
text-decoration: none;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.slip-receipt__link:hover {
|
|
438
|
+
color: var(--accent-deep);
|
|
439
|
+
text-decoration: underline;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.slip-receipt__foot {
|
|
443
|
+
margin: 0;
|
|
444
|
+
padding-top: 12px;
|
|
445
|
+
border-top: 1px solid var(--divider);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
@keyframes slip-pulse {
|
|
449
|
+
0%,
|
|
450
|
+
100% {
|
|
451
|
+
opacity: 1;
|
|
452
|
+
}
|
|
453
|
+
50% {
|
|
454
|
+
opacity: 0.45;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
@media (prefers-reduced-motion: reduce) {
|
|
459
|
+
.slip-card--loading {
|
|
460
|
+
animation: none;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* Named for a screen reader, absent for everyone else. */
|
|
465
|
+
.slip-hidden {
|
|
466
|
+
position: absolute;
|
|
467
|
+
width: 1px;
|
|
468
|
+
height: 1px;
|
|
469
|
+
margin: -1px;
|
|
470
|
+
padding: 0;
|
|
471
|
+
overflow: hidden;
|
|
472
|
+
clip-path: inset(50%);
|
|
473
|
+
white-space: nowrap;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/* Below 440px the card takes the 380 layout: a smaller icon and tighter rhythm. */
|
|
477
|
+
@container (max-width: 440px) {
|
|
478
|
+
.slip-card {
|
|
479
|
+
gap: var(--space-12);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.slip-card__icon,
|
|
483
|
+
.slip-card__alarm,
|
|
484
|
+
.slip-skeleton--icon {
|
|
485
|
+
width: 44px;
|
|
486
|
+
height: 44px;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.slip-card__action {
|
|
490
|
+
flex: 1 1 100%;
|
|
491
|
+
}
|
|
492
|
+
}
|