The one rule: contrast over colour
A QR code is a binary image. The scanner does not see "red and blue" or "navy and cream" — it sees "dark modules" and "light modules." The colour you pick matters only to the extent that it preserves that dark-vs-light distinction.
The specification that decides "dark enough" is the WCAG 2.1 contrast ratio, the same accessibility standard used for body text on websites. The math:
(L1 + 0.05) / (L2 + 0.05) where L1 is the relative luminance of the lighter colour and L2 is the darker colour, both as values from 0 (black) to 1 (white).
A ratio of 21:1 (pure black on pure white) is the safe baseline. WCAG AA requires 4.5:1 for accessible body text. For QR codes, 4.5:1 is also the practical reliability floor. Below 3:1, scan failures start climbing on anything other than a flagship phone in good light. Below 2:1, even the latest iPhone struggles.
The upside: any brand colour dark enough to pass WCAG accessibility for body text on white will scan reliably as the dark modules of a QR code. The downside: most marketing-driven colour choices (pastels, neons, light brand accents) fail that bar without anyone noticing until the printed batch is in the wild.
How we tested 24 colour combinations
In May 2026 we printed 24 QR codes encoding the same 60-character URL onto two paper stocks — 100lb glossy text and 80lb uncoated matte. The codes spanned three groups: the safe baseline (black on white, charcoal on cream, navy on ivory), brand-realistic accents (forest green on white, burgundy on cream, deep teal on ivory, dark purple on white, chocolate brown on cream), and known-problematic combos (yellow on white, lime on white, light grey on white, pastel pink on white, pale blue on white, navy on light blue, dark grey on charcoal).
We scanned each printed code with an iPhone 15 Pro (iOS 18), a Pixel 8 (Android 14), a Galaxy S24 Ultra (Android 14), and an iPhone 11 (iOS 18 — included specifically for the older-hardware case) under three lighting conditions: office overhead fluorescent (~500 lux), warm indoor lamp (~150 lux), and direct sunlight (~10,000 lux). Five scans per code per phone per lighting condition = 60 scans per colour pair.
We pre-computed the WCAG 2.1 contrast ratio for every pair using the standard luminance formula and then correlated that ratio with scan success across all 60 trials. The pattern was unambiguous: contrast ratio is the dominant variable. Paper stock and lighting matter at the margins; contrast decides whether you ever needed to worry about either.
What follows are the rules that fell out of those 60 × 24 = 1,440 scan attempts, plus the colour palettes we now recommend by default in our custom QR generator walk-through.
The contrast ratio cheat sheet
Use this table to vet any colour pair before you generate the code. Plug your two hex values into any WCAG contrast checker (WebAIM's is free and fast) and check the ratio.
| Contrast ratio | WCAG grade | QR scan reliability | Use it? |
|---|---|---|---|
| 7:1 or higher | AAA | Reliable on every phone, every lighting | Yes — preferred |
| 4.5:1 to 7:1 | AA | Reliable on phones <5 years old in normal light | Yes — safe default |
| 3:1 to 4.5:1 | AA Large only | Flaky on older phones and low light | Avoid for print |
| 2:1 to 3:1 | Fails WCAG | Frequent failures on iPhone 11 and older Android | No — will burn you |
| Below 2:1 | Fails WCAG | Fails on most phones | No — never |
Safe brand-colour palettes that pass 4.5:1 on white
You do not need to default to black to get a reliable QR. Plenty of brand-realistic dark colours clear the 4.5:1 bar on a white or near-white background. The list below is not exhaustive — any colour with relative luminance below ~0.2 will work on white. These are the ones that come up most often in real brand systems.
Navy and indigo: #001F54, #0A2540, #1B2A4E. Contrast on white: 12–17:1. Reads as professional and corporate without going to pure black.
Forest and dark green: #1B4332, #143620, #2D5016. Contrast on white: 9–13:1. Reads as eco-friendly, financial, or outdoorsy. Pairs well with cream and ivory backgrounds.
Burgundy and dark red: #641220, #8B0000, #5C0E1E. Contrast on white: 7–11:1. Good for hospitality, food, and editorial brands.
Deep purple and aubergine: #3D0066, #4A1A6E, #2C1B47. Contrast on white: 10–15:1. Underused — strong brand differentiation without sacrificing scan reliability.
Dark teal and slate: #0B3954, #2C3E50, #1F4E5F. Contrast on white: 8–12:1. Modern and calm; pairs well with light wood, kraft, or off-white backgrounds.
Chocolate brown: #3B2F2F, #4E342E, #5D4037. Contrast on white: 8–11:1. Honest, organic, premium. Works on kraft paper for packaging.
Charcoal (not pure black): #1A1A1A, #212121, #2C2C2C. Contrast on white: 15–18:1. Softer than #000000 for high-end print without losing reliability.
The pattern: any hex value where the average of R, G, and B is below ~50 (out of 255) will pass 4.5:1 on white. Above ~80, you start dropping below 4.5:1 and you need to test before committing.
The colours that quietly fail
These are the combinations we see customers attempt most often that fail in printed batches. Each one fell below 3:1 contrast in our test and produced scan failures on at least one of the four phones in at least one lighting condition.
Yellow on white (#FFD700 on #FFFFFF): 1.2:1 contrast. Failed on every phone in dim light, intermittent in office lighting, occasionally worked in direct sunlight. Avoid. This is the single most common "looks great in Figma, dies in print" failure mode.
Light grey on white (#CCCCCC on #FFFFFF): 1.6:1 contrast. Failed reliably on iPhone 11 and Pixel 8 in dim light. Worked on iPhone 15 Pro in bright light only. If the brand identity demands a "soft" feel, achieve it with paper texture, not low-contrast modules.
Pastel pink on white (#FFB6C1 on #FFFFFF): 1.5:1 contrast. Failed on iPhone 11 across all lighting; flaky on the flagships in dim light. Common in lifestyle and wellness brands and a frequent source of dead codes.
Pale blue on white (#A7C7E7 on #FFFFFF): 1.9:1 contrast. Marginal even on the iPhone 15 Pro. Reads as tech-y or "fresh" in mockups; reads as a non-functional decoration in print.
Lime green on white (#9ACD32 on #FFFFFF): 1.7:1 contrast. Despite looking dark on screen, the high green-channel luminance puts the contrast below 2:1. Failed on three of four phones in dim light.
Navy on light blue (#001F54 on #A7C7E7): 4.9:1 contrast. This one is interesting — it scrapes past WCAG AA, but it still produced scan failures because the *background colour* is closer to the module colour than the scanner expects for the "white" zone. ISO 18004 implementations assume the light zone is near-white; coloured backgrounds reduce the margin even when the contrast number looks fine. Keep the background within 10 luminance points of pure white.
Dark grey on charcoal (#404040 on #1A1A1A): 1.9:1 contrast. The inverted-dark-mode aesthetic. Looks moody, scans about half the time. If you need a dark aesthetic, use a light pattern on a very dark background, not two near-blacks.
The non-obvious lesson: the failure colours all looked fine on the designer's monitor. A monitor at 100% brightness flattens contrast perception. Always check the printed contrast ratio number, not the visual impression on screen.
Inverted QR codes (light on dark) — when they work
The ISO 18004 specification for QR codes assumes "dark on light." Most early scanners (and many cheap embedded scanners in 2026) implement only that case. Modern phone cameras with a current OS will invert if needed, but the support is not universal.
In our test, light-on-dark codes (white modules on a deep navy or near-black background) scanned reliably on the iPhone 15 Pro, Pixel 8, and Galaxy S24 Ultra in all lighting conditions. On the iPhone 11 they scanned in bright light but failed in dim light roughly 30% of the time. We did not test pre-2019 hardware but historical reports suggest scan rates drop further on iOS 12 and Android 8.
The honest call: inverted QRs are visually striking and work fine for digital-first audiences (an Instagram story, a presentation slide, a website hero image). For print campaigns where your audience may include an older iPhone or a flip-phone scanner, the older-device failure rate is high enough to disqualify inverted as a default.
If you need a dark aesthetic for a printed piece, two compromises preserve scan reliability:
1. Print a standard dark-on-light QR inside a dark-background frame. The QR itself is a contained light island within the larger dark layout. Most premium hotel and restaurant menus do this.
2. Use a very-dark module colour on a slightly-off-white module background (#0A0A0A on #F5F5F0). It reads as moody without inverting the polarity. Contrast remains above 15:1.
Gradients and multi-colour QRs
Modern generators including EZQR let you apply gradients across the dark modules of a QR code. The ISO standard only requires "dark zones to be distinguishable from light zones," so a single gradient (dark navy fading to dark purple, for instance) is technically compliant.
What works in our test: a gradient between two colours that both individually pass 4.5:1 contrast against the background. Navy-to-purple, forest-to-teal, burgundy-to-aubergine — all scanned reliably across phones.
What fails: a gradient where one end of the gradient drops below 4.5:1. We tested a navy-to-light-blue gradient where the "light" end scraped 2.8:1. The modules nearest the light end were the failure points — codes with logos placed at the gradient transition failed about 40% of the time on the iPhone 11. The fix is to clamp the lighter end of the gradient to a colour that still passes 4.5:1 by itself.
Multi-colour QRs (where individual modules are different colours, like a rainbow effect) are technically possible but practically dangerous. Each colour transition is a potential scanner-confusion zone. Skip this style for production print; reserve it for digital-only assets where you can replace the asset if a scan issue is reported.
For brand-coloured codes where you want personality without scan risk, the logo embedding approach is more reliable than gradients. A single dark module colour plus a centred brand logo plus error correction at level H gives you brand identity without playing chicken with the scanner.
Coloured backgrounds: when they ruin a perfectly good QR
Most colour discussions focus on the dark modules. The light zone — the "white" background — is where the second category of failures hides.
The ISO 18004 algorithm assumes the light zone is reflectance >70%. White is 100%. Cream and ivory hold at ~95%. Light grey at 85% is fine. Anything darker than 70% starts compressing the dynamic range the scanner needs.
The failure modes:
Coloured background with the brand's own dark colour on top. A navy module on a brand-grey background may read as 5:1 contrast on a contrast checker, but the scanner does not know that the brand grey is "supposed to be" the light zone. To the camera, it is a mid-tone, and the algorithm has less margin to tolerate ink-bleed, fingerprint smudges, or off-axis scanning. The same risk applies to bulk-printed assets where the background colour varies slightly batch to batch — see our bulk QR generator guide for the production-print quality controls.
Photo backgrounds. A QR placed directly over a marketing photograph is the most common print disaster. Even if the photo is desaturated or partially transparent, the variable luminance under each module corrupts the dark-vs-light read. Solution: place the QR inside a solid white (or near-white) box overlaid on the photo. The "quiet zone" margin (a four-module-wide light border around the entire code) must also fall on the solid background, not bleed onto the photo.
Patterned packaging. Same issue at smaller scale. The QR sits on solid colour; the surrounding label can be any pattern.
The quiet zone is the silent failure cause we see most often in packaging-label QR audits. A QR with perfect contrast in the modules but a pattern or photo running into the four-module border around it will fail in ways that look mysterious until you measure the quiet zone.
Print vs digital: the same colour, different reliability
A QR that scans flawlessly on a phone screen can fail on the same image printed at the same size on paper. Two reasons.
Ink versus pixels. A phone screen displays the dark modules as pure RGB-rendered colour with full pixel saturation. A print process — even high-quality digital printing — produces dark modules as CMYK ink dots with gain, dot loss at edges, and substrate absorption. The result is that the printed "dark" is consistently lighter than the digital "dark" by 5–15% luminance. A pair that measured 4.6:1 on screen may measure 4.0:1 on paper.
Substrate matters. Glossy paper preserves contrast better than uncoated matte. Kraft paper (brown) compresses contrast significantly — a code that scans on white may need the dark colour pushed to pure black to scan on kraft. Recycled paper with visible fibre flecks introduces local light-zone noise.
The practical workflow: pick the colour pair using the WCAG ratio on the digital design. Add a safety margin — aim for at least 6:1 ratio in the design if the final output is print. Then print one sample on the actual stock at the actual size and scan it with the oldest phone you have. If it scans cleanly in dim light, the batch is safe.
For large print runs (5,000+ units), the print production checklist in the packaging guide covers the substrate, ink, and proofing steps that catch the remaining failure modes.
Error correction is the safety net for colour risk
QR codes carry four error correction levels — L (7% recovery), M (15%), Q (25%), H (30%). Higher levels add redundancy that lets the scanner reconstruct the code even when parts are damaged, smudged, or — for our purposes — printed with reduced contrast.
The non-obvious lesson from our colour test: codes generated at error correction level H tolerated borderline contrast (3:1 to 4.5:1) on flagship phones, where the same codes at level L failed. Error correction does not fix below-2:1 contrast — it cannot make a yellow-on-white code scan — but it buys you margin in the 3–5:1 range that real brand work often lands in.
The trade-off: higher error correction adds modules to the code at the same data payload, making the code visually denser. A 60-character URL at level L might encode as a 25×25 module grid; the same URL at level H lands at 33×33 modules. For small printed sizes (under 1 inch), the higher module count means smaller individual modules, which interacts badly with substrate and ink-dot size.
The rule we apply: if your colour choice clears 4.5:1, use level Q. If it lands between 3:1 and 4.5:1 and you cannot change colours (locked brand guidelines), use level H and print a sample. Below 3:1, change the colours; error correction will not save you. The error correction deep dive covers the math behind the trade-off.
How to test before committing to a batch
The single highest-ROI step in the entire colour-decision workflow is a test print before the production run. The colour pair that looked safe on the contrast checker still earns 10 minutes of physical verification.
The checklist:
1. Generate the actual code with the actual URL. Not a placeholder. The destination URL length affects module density; a 30-character URL encodes differently from a 200-character UTM-tagged URL. Use the URL that will ship.
2. Print on the actual stock at the actual final size. A code printed at 2 inches on a home laser printer is not a fair test for a code that will appear at 0.5 inches on a glossy coated box. The combination of size, substrate, and print method is what determines reliability.
3. Scan with three phones across two lighting conditions. Ideally one current flagship (iPhone 15 Pro, Pixel 8, or newer), one mid-range (2-year-old Android), one older (iPhone 11 or older Android). Lighting: well-lit indoor (the easy case), dim indoor (the hard case). If you have access to a phone with a cracked or smudged camera lens, scan with that too — it simulates real-world conditions.
4. Try the off-axis scan. Customers do not always hold their phone perpendicular to the code. Scan at 30 degrees off-axis. A code with marginal contrast often fails the angled scan first.
5. Confirm the quiet zone. Hold a piece of white paper around the code so the four-module border is solid white. If the code scans with the white border and fails without, your packaging design needs a wider quiet zone.
If the test sample fails on any combination, fix it before the print run. Reprinting 10,000 packages because the brand pink was too pale costs more than picking navy in the first place.
The bottom line
A coloured QR works when the dark modules pass 4.5:1 WCAG contrast against a near-white background. Use any dark brand colour that meets the bar — navy, forest, burgundy, deep teal, charcoal, dark purple, chocolate. Avoid pastels, neons, light greys, and yellow on white; they look fine on a monitor and fail on a customer's phone.
Skip inverted (light on dark) codes for any print campaign with older-phone exposure. Skip gradients that touch the light end of the spectrum. Skip photo backgrounds without a solid quiet zone. Use error correction level Q or H for any colour pair that scrapes the 4.5:1 floor.
If you want a transparent background instead of a fixed light colour, the rules shift — see our transparent background QR guide for the additional traps to avoid. And if you want the code to keep working indefinitely without any vendor dependency, the permanent QR code guide covers the static-vs-dynamic decision tied to long-term reliability.
Test one printed sample with three phones before committing to the production batch. Ten minutes of verification beats reprinting a campaign.
For the actual generation, EZQR free supports any hex colour for the dark modules, lets you set the background colour explicitly, exports both PNG and SVG, and surfaces the contrast warning before you download. The Pro plan at $10/mo adds the brand-asset library if your team needs to keep the same locked colour across multiple campaigns. The QR generator comparison covers which tools surface contrast warnings before download. See the event QR codes use case for an example of brand-coloured event lanyards that survive the test.