Expand shorthand hex color
Turn shorthand into the long form browsers use: #f0a becomes #ff00aa because each digit is doubled, and you also get #FF00AA in capitals and the channels 255, 0 and 170. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Hex
- #f0a
- Hex
- #ff00aa
- Hex no hash
- ff00aa
- Uppercase
- #FF00AA
- Shorthand
- #f0a
CSS and design tools often write colors as three-digit shorthand such as hash f zero a instead of the full six-digit form hash f f zero zero a a. Browsers expand that shorthand by doubling every hex digit so each channel becomes a full byte. This expand shorthand hex color tool takes a three-digit code with or without a leading hash, validates that every character is a hexadecimal digit, and returns the deterministic six-digit expansion plus the corresponding red, green, and blue channel integers. Front-end developers use it when design tokens arrive as short codes and stylesheets, snapshots, or design systems require the long form. Students and QA scripts use it to check that a handwritten palette matches the CSS Color Module rule byte for byte.
What 3-digit shorthand means and how the expansion works
In CSS, a hex color may be written with three digits after an optional hash: one nibble for red, one for green, and one for blue. The official expansion rule is simple and absolute: each digit is repeated once so three characters become six. For example, hash a b c expands to hash a a b b c c, hash f zero a expands to hash f f zero zero a a, and hash zero zero zero expands to six zeros.
That rule is not a heuristic and not a perceptual approximation; it is the same mapping browsers apply when they parse a stylesheet. This calculator encodes exactly that mapping. There is no path that invents a fourth channel, preserves alpha, or treats 0x prefixes as valid CSS colors.
Use a dedicated hex-to-RGB or multi-format converter when you already have six digits and only need channel numbers; use this page when the input is deliberately short and the job is to materialize the long form with a clear validation contract.
Where expanding shorthand hex earns its keep
Design systems often receive brand swatches as three-digit codes from older style guides, Figma plugins, or hand-written notes, while modern token pipelines and CSS custom properties prefer six-digit hashes for grepping, snapshot diffs, and one-to-one channel mapping. Expanding shorthand once at the boundary keeps every downstream consumer on the long form without each team re-implementing the doubling rule slightly differently.
Teaching materials for web development introduce three-digit colors early because they are easy to type, then require students to explain the expansion; a deterministic tool lets instructors show that hash f f f is white and that hash f zero a is not the same as hash f zero a zero with missing digits invented. QA and accessibility pipelines sometimes store colors in mixed formats; normalizing short codes to six digits before contrast checks avoids false mismatches when one side used shorthand and the other used long form.
Pair this expander with RGB or HSL converters when you need other spaces, and keep this page focused when the only job is three digits to six under a strict validation contract that refuses already-long codes and illegal characters instead of guessing.
What you can do with it
Design tokens and CSS normalization
Convert brand #RGB shorthand into #RRGGBB strings for theme files, custom properties, and design-system token pipelines.
Teaching and homework checks
Show students the CSS nibble-doubling rule and verify hand-expanded colors without opaque calculator apps.
FAQ
How does #RGB expand to #RRGGBB?
Each of the three hex digits is doubled: #abc becomes #aabbcc, #f0a becomes #ff00aa. That is the same rule CSS browsers use for shorthand colors.
What characters are allowed?
Exactly three hexadecimal digits 0–9 and A–F/a-f, with an optional leading #. Spaces, 0x prefixes, empty strings, and non-hex characters are rejected.
What is #f0a as a six-digit color?
It is #ff00aa. Each of the three digits is written twice, f to ff, 0 to 00 and a to aa, which gives red 255, green 0 and blue 170.
Do I have to type the leading hash?
No. You can enter #f0a or just f0a and the result is the same. The answer also records whether your input had a hash.
Can I paste a color that already has six digits?
No. This tool only expands three-digit shorthand, so a six-digit code is turned down rather than passed through unchanged. To read the channels of a full code, use a hex to RGB converter.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/color/hex3-hex6 \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"hex":"#f0a"}'const res = await fetch("https://api.kit.forhosting.com/color/hex3-hex6", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"hex": "#f0a"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/color/hex3-hex6",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"hex": "#f0a"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/color/hex3-hex6", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"hex":"#f0a"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"hex":"#f0a"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/color/hex3-hex6", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"hex": "#f0a"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "color.hex3_hex6",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
The engine never calls the network, never samples randomness, and never depends on the clock, so free browser runs and API calls at $0.002 per successful request always agree on the same #RRGGBB string.
What 3-digit shorthand means and how the expansion works
You supply a three-digit shorthand, the engine lowercases the digits for a stable canonical form, doubles each nibble, and returns the full hash-prefixed string, the bare six digits without the hash, an uppercase hash form for style guides that prefer capital A through F, the normalized three-digit shorthand with and without a hash, a boolean recording whether the input carried a leading hash, and the integer channels r, g, and b in zero through two hundred fifty-five so you can feed the same color into RGB tooling without a second conversion. Wrong length, including a full six-digit code that is already expanded, is rejected with invalid_input rather than silently passed through, because the search intent of this page is the expand step itself, not a multi-format color round-trip.
Input contract, validation, and reproducible output shape
Outer whitespace is trimmed. After the optional hash, the body must contain exactly three characters drawn from zero through nine and A through F in either case. Spaces inside the body, empty bodies, four-digit or eight-digit forms, and any non-hex character fail validation. The preferred field name is hex, but aliases color, value, text, input, code, and shorthand are accepted so forms, query strings, and spreadsheets can reuse the same contract without renaming columns. The value must be a string: bare numbers are rejected because leading zeros and digit length matter for color codes and a JSON number cannot preserve those details. An optional single leading hash is allowed and recorded in had_hash; a 0x or 0X prefix is not CSS shorthand and fails with a clear message. On success the response always includes lowercase hex with a leading hash as the primary expanded value, hex_no_hash for token files that store bare digits, uppercase for capital-letter conventions, shorthand and shorthand_no_hash as the normalized three-digit input, channel integers r, g, and b derived from the expanded pairs, method tag hex3_to_hex6, and a short formula string restating that each nibble d becomes d d. Failed validation is not billed; successful API calls cost the published base price.
Integration use cases
Deterministic CI fixtures
Pin expanded hex results in unit tests and snapshot baselines so short palette codes never drift across environments.
Technical questions
Does the tool accept 6-digit hex as input?
No. This tool only expands 3-digit shorthand. A 6-digit code is rejected with invalid_input because it is already expanded; use a hex-to-RGB tool if you need channels from a long form.
Is the leading # required?
No. Both #f0a and f0a are accepted. The response includes had_hash so you can see whether the input used a hash, and hex always returns a hash-prefixed six-digit string.
What does expand shorthand hex color cost?
The browser tool is free. API requests use prepaid KIT balance at $0.002 per successful call; invalid input is not billed.