Dark Mode, PWA Support, and 3 New Tools
This guide has a free tool → Open JWT Decoder
# Dark Mode, PWA Support, and 3 New Tools: A Deep Dive
We just shipped a major update to ToolBox with features you have been asking for. This post covers what landed, how each feature works under the hood, and practical guidance on getting the most out of the three new tools - the JWT Decoder, the Cron Expression Parser, and the Color Palette Generator.
Dark Mode
ToolBox now fully supports dark mode. It automatically matches your system preference, and you can toggle it manually using the moon/sun icon in the header.
How the Dark Mode Implementation Works
Dark mode in modern web apps falls into one of three implementation patterns:
Pattern 1: CSS media query only
@media (prefers-color-scheme: dark) {
:root {
--background: #0f0f0f;
--text: #f5f5f5;
}
}This automatically respects system preferences but offers no manual override.
Pattern 2: JavaScript class toggle
document.documentElement.classList.toggle('dark');
localStorage.setItem('theme', isDark ? 'dark' : 'light');This allows manual toggle and persists the preference, but can cause a flash of the wrong theme on initial load if the script loads after the HTML renders.
Pattern 3: Inline script in `<head>` (the approach ToolBox uses)
The cleanest solution reads the user's saved preference before the page renders, preventing any flash:
<head>
<script>
(function() {
const saved = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (saved === 'dark' || (!saved && prefersDark)) {
document.documentElement.classList.add('dark');
}
})();
</script>
</head>By running this synchronously before any HTML is parsed, the correct theme class is on the <html> element before any CSS loads, eliminating the flash entirely.
Why Dark Mode Matters Beyond Aesthetics
Dark mode is not purely a visual preference. There are meaningful technical and ergonomic reasons developers favor it:
OLED screen efficiency: On OLED and AMOLED displays (common in modern phones and some monitors), pure black pixels consume no power. A dark-themed UI can meaningfully reduce battery consumption on these devices.
Reduced eye strain in low-light environments: Bright white screens emit significant light. In a dark room - like a home office at night - a white-background UI creates stark contrast with the surroundings that causes eye strain over extended sessions.
Blue light reduction: Light themes typically use white or very light gray backgrounds, which emit more blue-wavelength light. Blue light has been linked to disrupted sleep cycles when viewed in the hours before bed. Developers working late often prefer dark mode for this reason.
Contrast for code readability: Code editors have used dark backgrounds with light text since the terminal era. Many developers find syntax highlighting more legible against a dark background because the colors stand out more vividly.
Contrast Ratios and Accessibility
The dark theme in ToolBox is designed to meet WCAG 2.1 AA contrast standards. This means:
- Normal text: at least 4.5:1 contrast ratio against the background
- Large text (18px+ regular or 14px+ bold): at least 3:1 contrast ratio
- Interactive elements: at least 3:1 contrast ratio
You can verify any color combination using the Color Contrast Checker.
The WCAG contrast requirements apply equally to light and dark modes. A common mistake when implementing dark mode is testing contrast ratios only in light mode. The color palette must work in both contexts.
Common dark mode mistakes to avoid:
| Mistake | Problem | Fix |
|---|---|---|
| Pure white text on pure black | Causes halation, text bleeds | Use off-white (#E5E5E5) on near-black (#1A1A1A) |
| Inverting all colors naively | Images, charts, and shadows look wrong | Invert only UI elements, not content |
| Forgetting focus outlines | Invisible focused elements | Ensure focus rings have enough contrast in dark mode |
| Using opacity for disabled states | Low opacity can fail contrast | Use explicit dark-mode disabled colors |
Respecting prefers-reduced-motion
Alongside prefers-color-scheme, ToolBox also respects the prefers-reduced-motion media query. Users who have indicated in their OS settings that they prefer reduced motion will not see the transition animations that play during dark/light mode switching.
@media (prefers-reduced-motion: reduce) {
* {
transition: none !important;
animation: none !important;
}
}This is important for users with vestibular disorders, epilepsy, or other conditions where flashing or motion effects can cause discomfort or harm.
JWT Decoder
Free online JWT decoder - decode and inspect JSON Web Tokens without sending them to a server
Cron Expression Parser
Free online cron expression parser - parse cron expressions and see the next scheduled run times
Color Palette Generator
Free online color palette generator - generate beautiful color palettes from any base color
PWA Support
ToolBox can now be installed as a Progressive Web App on your phone or desktop. This gives you offline access to previously loaded tools, faster loading with cached assets, and an app-like experience without the browser chrome.
What a PWA Actually Is
A Progressive Web App is a web application that uses modern browser APIs to deliver capabilities traditionally associated with native apps. The "progressive" part means it enhances the experience for users with capable browsers while still working for everyone else.
The three foundational components of a PWA are:
1. HTTPS
PWAs require a secure origin. ToolBox runs on HTTPS through Vercel.
2. A Web App Manifest
The manifest is a JSON file that tells the browser how to present the app when installed:
{
"name": "ToolBox - Developer Tools",
"short_name": "ToolBox",
"description": "30+ free online tools for developers and designers",
"start_url": "/",
"display": "standalone",
"background_color": "#0f0f0f",
"theme_color": "#3B82F6",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}3. A Service Worker
The service worker is a JavaScript file that runs in the background, separate from the main page. It intercepts network requests and can serve cached responses when the network is unavailable.
How Service Worker Caching Works
When you first visit ToolBox, the service worker installs and caches the core assets:
const CACHE_NAME = 'toolbox-v1';
const CORE_ASSETS = [
'/',
'/manifest.json',
'/icons/icon-192.png',
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(CORE_ASSETS))
);
});On subsequent visits, when the browser requests a page or asset, the service worker intercepts that request:
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
if (cachedResponse) {
return cachedResponse; // Serve from cache instantly
}
return fetch(event.request); // Fall back to network
})
);
});This "cache first" strategy means that once you have loaded a tool, it will load from cache on the next visit even if your network connection is slow or offline.
Installing ToolBox as a PWA
On Chrome (desktop):
Look for the install icon in the address bar - it looks like a computer with a down arrow. Click it and select "Install." The app will appear in your applications menu and taskbar.
On Safari (iPhone/iPad):
Tap the Share button (the box with an arrow pointing up), then tap "Add to Home Screen." The app icon will appear on your home screen like a native app.
On Chrome (Android):
The browser will show an install banner at the bottom of the screen, or you can tap the three-dot menu and select "Add to Home Screen."
On Edge (desktop):
Click the three-dot menu in the top right, then "Apps," then "Install this site as an app."
Once installed, ToolBox launches in a standalone window without browser chrome - no address bar, no tab bar, just the app.
The Offline Experience
When you are offline and open an installed ToolBox PWA, you will see the tools that were previously cached. Since all tools process data locally, a tool that has been loaded at least once will continue to work fully offline. There is no server dependency for the actual tool functionality.
This is one of the more powerful aspects of the privacy-first design: because no data goes to a server, there is no server that needs to be available. The tool is entirely self-contained in your browser.
New Tool: JWT Decoder
Paste any JSON Web Token and instantly see the decoded header, payload, and signature. Check expiration times and inspect all claims - all without sending your token to any server.
Understanding JSON Web Tokens
JWTs are the dominant authentication mechanism in modern web applications. When you log in to an API-first application, the server typically responds with a JWT that you include in subsequent requests to prove your identity.
The structure of a JWT:
HEADER.PAYLOAD.SIGNATUREEach part is Base64URL-encoded (similar to Base64 but URL-safe). Let us decode a real example:
Encoded:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3IxMjMiLCJuYW1lIjoiQWxpY2UgU21pdGgiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAwMzYwMH0.abc123signatureDecoded header:
{
"alg": "HS256",
"typ": "JWT"
}Decoded payload:
{
"sub": "usr123",
"name": "Alice Smith",
"role": "admin",
"iat": 1700000000,
"exp": 1700003600
}From the payload we can immediately see:
- The user's ID is
usr123 - Their name is Alice Smith
- They have the
adminrole - The token was issued at Unix timestamp 1700000000
- It expires one hour later at 1700003600
The JWT Decoder converts those Unix timestamps into readable dates automatically, so you can immediately see whether a token has expired.
Common JWT Debugging Scenarios
Scenario 1: "Unauthorized" errors in API calls
When an API returns a 401 Unauthorized error, the first step is to decode the JWT you are sending. Check:
- Has the token expired? (Compare
expto current time) - Is the
aud(audience) claim set to the right value for this API? - Does the
iss(issuer) match what the API expects?
Scenario 2: Missing permissions
When an API returns a 403 Forbidden error, decode the token and check the custom claims your application uses for role or permission data. You may find the role claim is absent, or set to a value the endpoint does not accept.
Scenario 3: Token expiration debugging
If users are complaining they get logged out unexpectedly, decode a fresh token and check:
- What is the
expvalue? - What is the
iatvalue? - The difference between them is the token lifetime
// Calculate token lifetime in minutes
const payload = JSON.parse(atob(token.split('.')[1]));
const lifetimeMinutes = (payload.exp - payload.iat) / 60;
console.log(`Token is valid for ${lifetimeMinutes} minutes`);JWT Security Best Practices
Never decode JWTs in server code to verify them - verify the signature instead.
Decoding the payload without verifying the signature is a critical security mistake. Anyone can create a JWT with any payload if they do not need to know the signing key. The signature verification step is what prevents this.
// WRONG - decodes but does not verify
const payload = JSON.parse(atob(token.split('.')[1]));
const userId = payload.sub; // Do not trust this without verification!
// RIGHT - uses a library that verifies the signature
import { verify } from 'jsonwebtoken';
const payload = verify(token, process.env.JWT_SECRET);
const userId = payload.sub; // This is verifiedThe ToolBox JWT Decoder is a debugging tool for development and troubleshooting. In production code, always use a proper JWT library that verifies signatures.
Token storage on the client:
| Storage method | XSS risk | CSRF risk | Notes |
|---|---|---|---|
localStorage | High | None | XSS can read it |
sessionStorage | High | None | XSS can read it, cleared on tab close |
httpOnly cookie | None | Medium | JS cannot read it, need CSRF protection |
| In-memory (JS variable) | Low | None | Lost on page refresh |
For most applications, httpOnly cookies with SameSite protection offer the best security posture.
New Tool: Cron Expression Parser
Enter a cron expression and get a human-readable description plus the next 5 scheduled run times. Includes presets for common schedules.
Why Cron Expressions Are Hard to Read
Cron expressions are compact and powerful, but the syntax is not intuitive until you have used it for years. Consider this expression:
0 2 * * 1Without experience, this is opaque. The Cron Parser tells you immediately: "At 02:00 AM, only on Monday." That context turns debugging a scheduled job from a syntax lookup exercise into an instant understanding.
Full Cron Syntax Reference
┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12 or JAN-DEC)
│ │ │ │ ┌─ day of week (0-6 or SUN-SAT, 7 also = SUN)
│ │ │ │ │
* * * * *Special characters:
| Character | Meaning | Example |
|---|---|---|
* | Any value | * * * * * - every minute |
, | Value list | 0,30 * * * * - at :00 and :30 |
- | Range | 0 9-17 * * * - every hour from 9AM to 5PM |
/ | Step | */5 * * * * - every 5 minutes |
L | Last | 0 0 L * * - last day of month |
W | Weekday | 0 0 15W * * - nearest weekday to 15th |
# | Nth weekday | 0 0 * * 2#1 - first Tuesday of month |
The Most Common Cron Schedules
| Expression | Description | Typical use case |
|---|---|---|
* * * * * | Every minute | Health check pings |
*/5 * * * * | Every 5 minutes | Cache warming |
*/15 * * * * | Every 15 minutes | API polling |
0 * * * * | Every hour | Aggregation jobs |
0 */6 * * * | Every 6 hours | Data sync |
0 0 * * * | Daily at midnight | Cleanup jobs |
0 9 * * * | Daily at 9 AM | Digest emails |
0 9 * * 1-5 | Weekdays at 9 AM | Business hour reports |
0 0 * * 0 | Every Sunday midnight | Weekly summaries |
0 0 1 * * | First of every month | Monthly billing |
0 0 1 1 * | January 1st | Annual archiving |
Crontab vs Application-Level Schedulers
Cron expressions appear in several contexts beyond the Unix crontab:
GitHub Actions:
on:
schedule:
- cron: '0 2 * * 1' # Every Monday at 2 AM UTCAWS EventBridge (Lambda triggers):
cron(0 2 ? * MON *)Note: AWS uses a 6-field format with an additional year field and different day-of-week syntax.
Node.js with node-cron:
import cron from 'node-cron';
cron.schedule('0 2 * * 1', () => {
console.log('Running weekly maintenance at 2 AM Monday');
});Kubernetes CronJobs:
spec:
schedule: "0 2 * * 1"Timezone Considerations
A critical gotcha with cron jobs: most cron implementations run in the server's local timezone, which may not be what you expect.
If your server is in UTC and your users are in US Eastern time (UTC-5), a job at 0 9 * * * runs at 9 AM UTC, which is 4 AM Eastern. During the initial debugging of a "scheduled email not arriving" bug, the timezone mismatch is often the culprit.
Best practice: always run cron jobs in UTC and convert to the target timezone in your application code. This makes the schedule predictable regardless of server location or daylight saving changes.
For building cron expressions visually without memorizing syntax, try the Crontab Generator - the visual companion to the parser.
New Tool: Color Palette Generator
Enter any base color and get harmonious palettes based on color theory - complementary, analogous, triadic, split-complementary, and monochromatic schemes.
The Color Theory Behind Palette Generation
Professional color palettes are not chosen arbitrarily. They are based on geometric relationships between colors on the color wheel. Understanding these relationships explains why the generated palettes work.
The color wheel in HSL terms:
Hue in HSL goes from 0 to 360 degrees, cycling through red (0), yellow (60), green (120), cyan (180), blue (240), magenta (300), and back to red (360). Color relationships are angular relationships on this wheel.
Palette types and their rules:
Complementary - Two colors directly opposite on the wheel (180° apart).
Base: hsl(220, 70%, 50%) - blue
Complement: hsl(40, 70%, 50%) - orange-yellowComplementary pairs create maximum contrast. They are energetic and attention-grabbing, which is why they appear in sports team logos and warning signs. In UI design, use the complement sparingly as an accent.
Analogous - Three to five colors adjacent on the wheel (30° apart).
Base: hsl(220, 70%, 50%)
Left: hsl(190, 70%, 50%) - teal
Right: hsl(250, 70%, 50%) - purple-blueAnalogous palettes are harmonious and natural. They appear throughout the natural world - the colors of a sunset, the shades of a forest. They work well for backgrounds and large surface areas.
Triadic - Three colors evenly spaced (120° apart).
Base: hsl(0, 70%, 50%) - red
Second: hsl(120, 70%, 50%) - green
Third: hsl(240, 70%, 50%) - blueTriadic palettes offer strong visual contrast while maintaining balance. They work well when you need a vibrant, multi-color design that does not feel random.
Split-complementary - The base color plus two colors adjacent to its complement.
Base: hsl(220, 70%, 50%) - blue
Split 1: hsl(25, 70%, 50%) - orange (left of complement)
Split 2: hsl(55, 70%, 50%) - yellow (right of complement)This is a safer version of the complementary palette. It has high contrast but is more nuanced and less jarring.
Tetradic (Square) - Four colors evenly spaced (90° apart).
Color 1: hsl(0, 70%, 50%) - red
Color 2: hsl(90, 70%, 50%) - green-yellow
Color 3: hsl(180, 70%, 50%) - cyan
Color 4: hsl(270, 70%, 50%) - purpleTetradic palettes are rich but difficult to balance. When using them, choose one color as dominant and use the others as accents.
Monochromatic - Multiple shades and tints of a single hue.
Base: hsl(220, 70%, 50%)
Lighter: hsl(220, 70%, 70%)
Lightest: hsl(220, 70%, 90%)
Darker: hsl(220, 70%, 30%)
Darkest: hsl(220, 70%, 10%)Monochromatic palettes are elegant and cohesive. They work exceptionally well for UI components where you need multiple states (hover, active, disabled) of the same color.
Applying Color Theory in CSS
Once you have generated a palette, here is how to implement it effectively in CSS:
:root {
/* Monochromatic palette based on brand blue */
--color-50: hsl(220, 90%, 97%);
--color-100: hsl(220, 80%, 92%);
--color-200: hsl(220, 75%, 82%);
--color-300: hsl(220, 72%, 70%);
--color-400: hsl(220, 70%, 60%);
--color-500: hsl(220, 70%, 50%); /* Base */
--color-600: hsl(220, 72%, 42%);
--color-700: hsl(220, 75%, 34%);
--color-800: hsl(220, 80%, 26%);
--color-900: hsl(220, 90%, 18%);
--color-950: hsl(220, 95%, 10%);
/* Accent from complementary palette */
--accent: hsl(40, 70%, 50%);
}
/* Usage */
.button-primary {
background: var(--color-500);
color: white;
}
.button-primary:hover {
background: var(--color-600);
}
.button-primary:active {
background: var(--color-700);
}
.badge-warning {
background: var(--accent);
color: var(--color-900);
}Color Accessibility Considerations
A generated color palette is aesthetically harmonious, but you must verify that the colors you use for text and interactive elements meet WCAG contrast requirements. Aesthetic and accessible are not the same thing.
Key rules:
- Never rely on color alone to convey information (add icons or text labels)
- Text must meet 4.5:1 contrast ratio against its background (3:1 for large text)
- Decorative elements do not need to meet contrast requirements
Run your text and background color combinations through the Color Contrast Checker before finalizing your design.
Using Palettes for Data Visualization
When building charts and graphs, color choice significantly affects readability. Guidelines for data visualization palettes:
- Use no more than 7-8 distinct colors (human visual processing limitation)
- Choose colors that remain distinguishable when converted to grayscale (for printing)
- Avoid red-green combinations (8% of males have red-green color blindness)
- Use sequential palettes (light to dark) for data with a natural order
- Use diverging palettes (two hues meeting at a neutral center) for data with a meaningful midpoint
The Color Palette Generator helps you quickly explore the color space and find combinations that work. Export the HEX values and plug them directly into your CSS variables or design tokens.
Summary of Updates
This update added three significant pieces of functionality:
Dark mode - Automatic system preference detection with manual override, implemented without flash of incorrect theme, with careful attention to WCAG contrast ratios in both light and dark contexts.
PWA support - Service worker caching for offline use, web app manifest for installability, and an app-like experience on desktop and mobile.
JWT Decoder - A safe, client-side tool for inspecting JSON Web Token headers, payloads, and expiration times. Essential for debugging authentication flows.
Cron Expression Parser - Human-readable descriptions of cron syntax with next-run-time previews. Saves the mental overhead of reading a six-field cron string every time you encounter one.
Color Palette Generator - Algorithmic palette generation based on color theory. Complementary, analogous, triadic, split-complementary, and monochromatic schemes from any starting color.
Try all of these tools at toolbox-kit.com. The JWT Decoder is available immediately without any setup. The Cron Parser and Color Palette Generator are likewise open directly from the homepage.
You might also like
Want higher limits, batch processing, and AI tools?