Skip to content

Commit 5fa6fca

Browse files
committed
feat: Pico v2 customized design system
1 parent 1a50fa6 commit 5fa6fca

20 files changed

+3643
-0
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ A pure HTML example, without dependencies.
1515
- **[Class-less preview](https://codesandbox.io/s/github/picocss/examples/tree/master/v2-html-classless)**
1616
A class-less example, without dependencies.
1717

18+
- **[Customized design system](https://codesandbox.io/s/github/picocss/examples/tree/master/v2-sass-customized-design-system)**
19+
An example to customize Pico CSS with SASS.
20+
1821
- **[React Class-less login](https://codesandbox.io/s/github/picocss/examples/tree/master/v2-react-classless-login)**
1922
A minimal, class-less login page in React, with a custom primary color.
2023
</details>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules/
2+
3+
# Build folder
4+
/css/
5+
6+
# Logs
7+
*.log
8+
9+
# System files
10+
.DS_Store
11+
Thumbs.db
12+
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<a href="https://picocss.com/">
2+
<img src="https://picocss.com/img/logo.svg" width="64" height="64">
3+
</a>
4+
5+
# Customized design system
6+
| Pico version | Tech stack |
7+
| ----- | ----- |
8+
| 2 | HTML, Sass |
9+
10+
An example to customize Pico CSS with SASS.
11+
12+
[![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/picocss/examples/tree/master/v2-sass-customized-design-system)
14.7 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
<meta name="color-scheme" content="light dark" />
7+
<title>Customized design system • Pico CSS</title>
8+
<meta name="description" content="A pure HTML example, without dependencies." />
9+
<link rel="stylesheet" href="css/main.css" />
10+
</head>
11+
12+
<body>
13+
<main>
14+
<svg viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
15+
<path
16+
d="M63.04 39.741c-4.275 17.143-21.638 27.576-38.783 23.301C7.12 58.768-3.313 41.404.962 24.262 5.234 7.117 22.597-3.317 39.737.957c17.144 4.274 27.576 21.64 23.302 38.784Z"
17+
fill="#F7931A"
18+
/>
19+
<path
20+
d="M46.11 27.441c.636-4.258-2.606-6.547-7.039-8.074l1.438-5.768-3.51-.875-1.4 5.616c-.924-.23-1.872-.447-2.814-.662l1.41-5.653-3.509-.875-1.439 5.766c-.764-.174-1.514-.346-2.242-.527l.004-.018-4.842-1.209-.934 3.75s2.605.597 2.55.634c1.422.355 1.68 1.296 1.636 2.042l-1.638 6.571c.098.025.225.061.365.117l-.37-.092-2.297 9.205c-.174.432-.615 1.08-1.609.834.035.051-2.552-.637-2.552-.637l-1.743 4.019 4.57 1.139c.85.213 1.682.436 2.502.646l-1.453 5.834 3.507.875 1.44-5.772c.957.26 1.887.5 2.797.726l-1.434 5.745 3.511.875 1.453-5.823c5.987 1.133 10.49.676 12.384-4.739 1.527-4.36-.076-6.875-3.226-8.515 2.294-.529 4.022-2.038 4.483-5.155ZM38.086 38.69c-1.085 4.36-8.426 2.003-10.806 1.412l1.928-7.729c2.38.594 10.012 1.77 8.878 6.317Zm1.086-11.312c-.99 3.966-7.1 1.951-9.082 1.457l1.748-7.01c1.982.494 8.365 1.416 7.334 5.553Z"
21+
fill="#fff"
22+
/>
23+
</svg>
24+
<form>
25+
<label for="amount">Amount to Send in Bitcoin</label>
26+
<input
27+
id="amount"
28+
name="amount"
29+
type="number"
30+
min="0.001"
31+
value="0.01"
32+
step="0.001"
33+
placeholder="0.01"
34+
required
35+
/>
36+
<small data-type="amount"><span data-type="usd" id="amount-usd"></span></small>
37+
<label for="address">Recipient's Bitcoin Address </label>
38+
<input
39+
id="address"
40+
name="address"
41+
type="text"
42+
placeholder="Enter Bitcoin address"
43+
autocomplete="off"
44+
required
45+
/>
46+
<fieldset>
47+
<label for="priority">Network Priority</label>
48+
<input
49+
id="priority"
50+
name="priority"
51+
list="priorities"
52+
type="range"
53+
min="1"
54+
max="5"
55+
step="1"
56+
value="3"
57+
list="priority"
58+
/>
59+
<datalist id="priorities">
60+
<option value="1">Min</option>
61+
<option value="2">Low</option>
62+
<option value="3">Medium</option>
63+
<option value="4">High</option>
64+
<option value="5">Max</option>
65+
</datalist>
66+
</fieldset>
67+
<table>
68+
<tbody>
69+
<tr>
70+
<td>Transaction Fees</td>
71+
<td>
72+
<div data-type="amount">
73+
<span id="transaction-fees-btc" data-type="btc"></span>
74+
<span id="transaction-fees-usd" data-type="usd"></span>
75+
</div>
76+
</td>
77+
</tr>
78+
</tbody>
79+
<tfoot>
80+
<tr>
81+
<td>Total Received</td>
82+
<td>
83+
<div data-type="amount">
84+
<span id="total-received-btc" data-type="btc"></span>
85+
<span id="total-received-usd" data-type="usd"></span>
86+
</div>
87+
</td>
88+
</tr>
89+
</tfoot>
90+
</table>
91+
<button type="submit">Send</button>
92+
</form>
93+
</main>
94+
<script src="js/main.js"></script>
95+
</body>
96+
</html>
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Initialize
2+
let bitcoinPrice = 0;
3+
let recommendedFees = {
4+
minimumFee: 20,
5+
economyFee: 40,
6+
hourFee: 60,
7+
halfHourFee: 70,
8+
fastestFee: 80,
9+
};
10+
const transactionSizeInBytes = 400;
11+
12+
// Form targets
13+
const form = document.querySelector("form");
14+
const addressInput = form.querySelector("#address");
15+
const amountInput = form.querySelector("#amount");
16+
const priorityInput = form.querySelector("#priority");
17+
const priorityOptions= form.querySelectorAll("#priorities option");
18+
19+
// Amounts targets
20+
const table = form.querySelector("table");
21+
const amountUsd = form.querySelector("#amount-usd");
22+
const transactionFeesBtc = table.querySelector("#transaction-fees-btc");
23+
const transactionFeesUsd = table.querySelector("#transaction-fees-usd");
24+
const totalReceivedBtc = table.querySelector("#total-received-btc");
25+
const totalReceivedUsd = table.querySelector("#total-received-usd");
26+
27+
// Fetch Data
28+
const fetchData = async (url) => {
29+
const response = await fetch(url);
30+
return await response.json();
31+
};
32+
33+
// Calculate Transaction
34+
const calculateTransaction = () => {
35+
const amount = amountInput.value;
36+
const priority = priorityInput.value;
37+
const feesInSats = calculateFeesInSats(priority);
38+
const feeInBtc = feesInSats / 100_000_000;
39+
updateUI(amount, feeInBtc);
40+
};
41+
42+
// Calculate Fees in Sats
43+
const calculateFeesInSats = (priority) => {
44+
const fees = ["minimumFee", "economyFee", "hourFee", "halfHourFee", "fastestFee"];
45+
return transactionSizeInBytes * recommendedFees[fees[priority - 1]];
46+
};
47+
48+
// Format Amount
49+
const formatAmount = (num, maximumFractionDigits = 2) => {
50+
const formatter = new Intl.NumberFormat("en-US", {
51+
notation: "compact",
52+
compactDisplay: "short",
53+
maximumFractionDigits,
54+
});
55+
return formatter.format(num);
56+
};
57+
58+
// Update UI
59+
const updateUI = (amount, feeInBtc) => {
60+
transactionFeesBtc.textContent = `-${formatAmount(feeInBtc, 8)} BTC`;
61+
totalReceivedBtc.textContent = `${formatAmount(amount - feeInBtc, 8)} BTC`;
62+
if (bitcoinPrice !== 0) {
63+
const feeInUsd = feeInBtc * bitcoinPrice;
64+
amountUsd.textContent = `${formatAmount(amount * bitcoinPrice)} USD`;
65+
transactionFeesUsd.textContent = `-${formatAmount(feeInUsd)} USD`;
66+
totalReceivedUsd.textContent = `${formatAmount(amount * bitcoinPrice - feeInUsd)} USD`;
67+
}
68+
};
69+
70+
// Set the priority style
71+
const setPriorityStyle = () => {
72+
const { value, min, max } = priorityInput;
73+
const percent = ((value - min) / (max - min)) * 100;
74+
priorityInput.style.setProperty("--pico-selected-ratio", `${percent}%`);
75+
};
76+
77+
// Handle Priority
78+
const handlePriority = () => {
79+
setPriorityStyle();
80+
calculateTransaction();
81+
};
82+
83+
// Handle Amount
84+
const handleAmount = () => {
85+
calculateTransaction();
86+
};
87+
88+
// Handle Priority Option
89+
const handlePriorityOption = (event) => {
90+
priorityInput.value = event.target.value;
91+
setPriorityStyle();
92+
calculateTransaction();
93+
};
94+
95+
// Listens for input changes
96+
amountInput.addEventListener("input", handleAmount);
97+
priorityInput.addEventListener("input", handlePriority);
98+
99+
// Fetch data and calculate transaction on load
100+
(async () => {
101+
const prices = await fetchData("https://mempool.space/api/v1/prices");
102+
bitcoinPrice = prices.USD;
103+
recommendedFees = await fetchData("https://mempool.space/api/v1/fees/recommended");
104+
calculateTransaction();
105+
})();
106+
107+
// Listen clicks on priority options
108+
priorityOptions.forEach((option) => {
109+
console.log(option);
110+
option.addEventListener("click", handlePriorityOption);
111+
});
112+
113+
// Set the priority style on load
114+
setPriorityStyle();
115+
116+
// Prevent form submission
117+
form.addEventListener("submit", (event) => {
118+
event.preventDefault();
119+
});
120+
121+
// Focus and move cursor to the end of the input on load
122+
(function focusAndMoveCursorToEnd(input) {
123+
input.focus();
124+
const value = input.value;
125+
input.value = "";
126+
input.value = value;
127+
})(amountInput);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "v2-sass-customized-design-system",
3+
"version": "1.0.0",
4+
"license": "MIT",
5+
"private": true,
6+
"main": "index.html",
7+
"scripts": {
8+
"start": "npm-run-all build serve",
9+
"build": "npm-run-all sort-scss css",
10+
"sort-scss": "postcss --config scss ./scss/**/*.scss --replace",
11+
"css": "npm-run-all css-compile css-prefix",
12+
"css-compile": "sass --style compressed --source-map --embed-sources --no-error-css --load-path=node_modules/@picocss/pico/scss/ scss/:css/",
13+
"css-prefix": "postcss --replace css/main.css --use autoprefixer --map",
14+
"dev": "npm-run-all --parallel watch browser-sync",
15+
"watch": "nodemon -e html,scss -x \"npm run build\"",
16+
"browser-sync": "browser-sync --no-ui --no-notify start --server --files='*.html, css/*.css'"
17+
},
18+
"devDependencies": {
19+
"@picocss/pico": "2.0.0-rc2",
20+
"autoprefixer": "^10.4.17",
21+
"browser-sync": "^3.0.2",
22+
"caniuse-lite": "1.0.30001579",
23+
"css-declaration-sorter": "^7.1.1",
24+
"nodemon": "^3.0.3",
25+
"npm-run-all": "^4.1.5",
26+
"postcss": "^8.4.33",
27+
"postcss-cli": "^11.0.0",
28+
"postcss-scss": "^4.0.9",
29+
"prettier": "^3.2.4",
30+
"sass": "^1.70.0",
31+
"serve": "^14.2.1"
32+
},
33+
"browserslist": [
34+
"defaults"
35+
]
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"hardReloadOnChange": true,
3+
"template": "node",
4+
"startScript": "serve",
5+
"container": {
6+
"node": "18"
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
@use "sass:map";
2+
@use "settings" as *;
3+
4+
[data-type="amount"] {
5+
display: flex;
6+
flex-direction: column;
7+
align-items: flex-end;
8+
font-variant-numeric: tabular-nums;
9+
10+
@media (min-width: map.get(map.get($breakpoints, "sm"), "breakpoint")) {
11+
flex-direction: row-reverse;
12+
align-items: center;
13+
14+
[data-type="usd"] {
15+
margin-right: var(#{$css-var-prefix}spacing);
16+
}
17+
}
18+
19+
[data-type="usd"] {
20+
color: var(#{$css-var-prefix}muted-color);
21+
}
22+
23+
// Loading
24+
[data-type="btc"],
25+
[data-type="usd"] {
26+
&:empty {
27+
display: inline-block;
28+
height: calc(1em * var(#{$css-var-prefix}line-height));
29+
opacity: 0.25;
30+
31+
&:after {
32+
display: inline-block;
33+
width: 4rem;
34+
height: 50%;
35+
transform: translateY(50%);
36+
border-radius: 0.25rem;
37+
background-color: currentColor;
38+
content: "\00a0 ";
39+
animation-duration: 2s;
40+
animation-iteration-count: infinite;
41+
animation-fill-mode: both;
42+
animation-name: opacity-pulse;
43+
opacity: 1;
44+
}
45+
}
46+
47+
@keyframes opacity-pulse {
48+
50% {
49+
opacity: 0.5;
50+
}
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)