mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-09 04:22:01 +00:00
docs: add more about theme providing
This commit is contained in:
24
content/blog/explaining-reacts-cache-function/react-theme-cache/.gitignore
vendored
Normal file
24
content/blog/explaining-reacts-cache-function/react-theme-cache/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>React Theme Cache</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
2426
content/blog/explaining-reacts-cache-function/react-theme-cache/package-lock.json
generated
Normal file
2426
content/blog/explaining-reacts-cache-function/react-theme-cache/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@unicorn-utterances/react-theme-cache",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "18.3.0-canary-0cdfef19b-20231211",
|
||||
"react-dom": "18.3.0-canary-0cdfef19b-20231211"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"vite": "^5.0.10"
|
||||
}
|
||||
}
|
||||
46
content/blog/explaining-reacts-cache-function/react-theme-cache/src/colors.js
vendored
Normal file
46
content/blog/explaining-reacts-cache-function/react-theme-cache/src/colors.js
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
function hexToRgb(hex) {
|
||||
// Remove the hash character
|
||||
hex = hex.replace(/^#/, "");
|
||||
|
||||
// Parse the hex value into separate R, G, B values
|
||||
const bigint = parseInt(hex, 16);
|
||||
const r = (bigint >> 16) & 255;
|
||||
const g = (bigint >> 8) & 255;
|
||||
const b = bigint & 255;
|
||||
|
||||
return { r, g, b };
|
||||
}
|
||||
|
||||
function rgbToHex(r, g, b) {
|
||||
// Convert RGB values to a hex color code
|
||||
return `#${((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1)}`;
|
||||
}
|
||||
|
||||
export function generateComplimentaryColors(hexColor) {
|
||||
const baseColor = hexToRgb(hexColor);
|
||||
|
||||
// Calculate the complementary color by inverting each RGB component
|
||||
const complimentary1 = rgbToHex(
|
||||
255 - baseColor.r,
|
||||
255 - baseColor.g,
|
||||
255 - baseColor.b,
|
||||
);
|
||||
|
||||
// Optionally, you can adjust the following multipliers for different shades
|
||||
const multiplier = 0.8;
|
||||
const complimentary3 = rgbToHex(
|
||||
Math.floor(baseColor.r * (1 + multiplier)),
|
||||
Math.floor(baseColor.g * (1 + multiplier)),
|
||||
Math.floor(baseColor.b * (1 + multiplier)),
|
||||
);
|
||||
|
||||
return [complimentary1, complimentary3];
|
||||
}
|
||||
|
||||
export const getReadableColor = (hexcolor) => {
|
||||
const r = parseInt(hexcolor.substr(1, 2), 16);
|
||||
const g = parseInt(hexcolor.substr(3, 2), 16);
|
||||
const b = parseInt(hexcolor.substr(5, 2), 16);
|
||||
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
return yiq >= 128 ? "#000000" : "#ffffff";
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { cache, useState } from "react";
|
||||
import { generateComplimentaryColors, getReadableColor } from "./colors.js";
|
||||
import "./style.css";
|
||||
|
||||
const getTheme = cache((primaryColor) => {
|
||||
// Theoretically, this could get very expensive to compute
|
||||
// Depending on how many colors and how accurately
|
||||
const [secondaryColor, tertiaryColor] =
|
||||
generateComplimentaryColors(primaryColor);
|
||||
return {
|
||||
primaryColor,
|
||||
secondaryColor,
|
||||
tertiaryColor,
|
||||
primaryTextColor: getReadableColor(primaryColor),
|
||||
secondaryTextColor: getReadableColor(secondaryColor),
|
||||
tertiaryTextColor: getReadableColor(tertiaryColor),
|
||||
};
|
||||
});
|
||||
|
||||
const capitalize = (str) => str[0].toUpperCase() + str.slice(1);
|
||||
|
||||
function ThemePreviewRow({ type, themeColor }) {
|
||||
// The calculations to get the theme only occur once, even though this is
|
||||
// called in multiple component instances.
|
||||
const theme = getTheme(themeColor);
|
||||
return (
|
||||
<tr>
|
||||
<th>{capitalize(type)}</th>
|
||||
<td>
|
||||
<div
|
||||
className="colorBox"
|
||||
style={{
|
||||
backgroundColor: theme[type + "Color"],
|
||||
color: theme[type + "TextColor"],
|
||||
}}
|
||||
>
|
||||
Some Text
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [themeColor, setThemeColor] = useState("#7e38ff");
|
||||
const [tempColor, setTempColor] = useState(themeColor);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="spaceBottom">
|
||||
<div className="spaceBottom">
|
||||
<label>
|
||||
<div className="spaceBottom">Primary color</div>
|
||||
<input
|
||||
type="color"
|
||||
id="body"
|
||||
name="body"
|
||||
value={tempColor}
|
||||
onChange={(e) => setTempColor(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={() => setThemeColor(tempColor)}>Set theme</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<table>
|
||||
<tbody>
|
||||
<ThemePreviewRow type="primary" themeColor={themeColor} />
|
||||
<ThemePreviewRow type="secondary" themeColor={themeColor} />
|
||||
<ThemePreviewRow type="tertiary" themeColor={themeColor} />
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
createRoot(document.getElementById("root")).render(<App />);
|
||||
@@ -0,0 +1,16 @@
|
||||
.spaceBottom {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.colorBox {
|
||||
margin-left: 0.5rem;
|
||||
min-height: 25px;
|
||||
min-width: 100px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px solid #3c3c3c;
|
||||
border-radius: 8px;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
});
|
||||
Reference in New Issue
Block a user