From default Esri blue to polished, branded, accessible apps. Every styling tool EB gives you, explained from the ground up.
If your app still uses the default Esri blue theme, your users can tell it's a government app from a mile away. Let's fix that.
You can have the best data in the world, the most thoughtful map layers, and perfectly configured widgets. None of it matters if your app looks like a default template. People judge credibility by appearance. A well-styled app gets used; an ugly one gets bookmarked and forgotten.
Experience Builder gives you three layers of styling control, and understanding how they interact is the single most important concept in this guide.
Imagine getting dressed. Your theme is your wardrobe palette — all navy and white, or all earth tones. Your widget Style tab is choosing specific clothes from that palette for each occasion. And custom CSS is taking a shirt to a tailor for a perfect fit. Most people only need the first two layers. The third is for when you need pixel-perfect control.
Sets colors, fonts, and base styles for the entire app. Change one setting, every widget updates. This is your foundation.
Override theme defaults for individual widgets. Add backgrounds, borders, shadows, and border-radius per widget.
Write raw CSS to target specific elements. The nuclear option — powerful but fragile across EB updates.
| Layer | Scope | Skill Needed | Stability |
|---|---|---|---|
| Theme | Entire app | None (UI only) | Fully supported |
| Widget Style tab | One widget | None (UI only) | Fully supported |
| Custom CSS | Any element | CSS knowledge | May break on EB updates |
Always start with the theme, then use the Style tab, and only reach for custom CSS when the first two layers genuinely cannot do what you need. This order saves you from brittle apps that break when Esri ships updates.
A theme is a coordinated set of colors, fonts, and sizing rules that apply globally to every widget in your app. When you pick a theme, you are not just choosing a color — you are choosing a personality.
EB ships with about 12 built-in themes. They fall into two categories:
Do not spend 45 minutes customizing widget colors, THEN switch themes. Switching themes resets most widget-level color overrides. Always pick your theme first, then customize widgets. Theme is the foundation — you pour it before building the walls.
Open any EB app in the builder. Hover over every single built-in theme to preview it. Pick one that feels closest to your goal, even if it is not perfect yet. We will customize it in the next lesson.
Every EB theme defines a handful of named color roles. Understanding these roles — not just the hex values — is what separates a polished app from one where you randomly picked colors until it "looked okay."
| Color Role | Where It Appears | Typical Use |
|---|---|---|
| Primary | Headers, nav bars, active tabs, primary buttons | Your brand's main color or the dominant UI tone |
| Secondary | Secondary buttons, tags, badges, highlights | Complements primary — should contrast but not clash |
| Light | Page backgrounds, card backgrounds | The "paper" your content sits on |
| Dark | Text, header bars, footers | The "ink" — high contrast against Light |
| Info / Accent | Links, interactive highlights, selected states | Draws attention to interactive elements |
| Success | Positive status, complete indicators | Green family — signals "all good" |
| Warning | Caution messages, attention-needed states | Orange/yellow family — signals "look here" |
| Danger | Error states, destructive actions | Red family — signals "something is wrong" |
Color roles are like actor roles in a movie. You do not cast the same actor as both the hero and the villain. Primary is your hero color — it appears the most. Secondary is the supporting actor. Danger is the villain. Each has a job, and when you swap colors between roles, the whole story falls apart.
#2D5F8A) or use the visual picker60% neutral (Light/Dark background), 30% primary (headers, sidebars, large areas), 10% accent (buttons, links, highlights). This ratio is used by every professional design system in the world. If your app feels "off" but you cannot explain why, check your ratios.
Typography is half of visual design. You can have perfect colors and still have an app that feels amateur if your fonts are wrong. EB gives you control over font family, heading styles, and body text — all from the Theme panel.
Used for widget titles, section headers, and large text. Choose something with personality and good weight variation.
Used for paragraphs, labels, and smaller text. Prioritize readability over style. Sans-serif is almost always the right call.
| Font | Best For | Personality |
|---|---|---|
| Avenir Next | General purpose, dashboards | Clean, modern, professional. Esri's default for good reason |
| Noto Sans | International apps, multilingual | Neutral, supports massive character sets |
| Roboto | Data-heavy apps | Compact, efficient, "Google-ish" |
| Merriweather | Story maps, narrative apps | Elegant serif, feels like a magazine |
| Oswald | Bold headers only | Tall, condensed, grabs attention. Do NOT use for body text |
Size creates hierarchy. If everything is the same size, nothing is important. Use 3-4 distinct sizes: page title (24-32px), section header (18-22px), body text (14-16px), captions/labels (11-13px). Maintain consistent ratios throughout the app.
The Theme sets global defaults. The Style tab on each widget lets you override those defaults for just that widget. This is where 80% of your actual styling work happens.
Every widget in EB has a Style tab in its settings panel. Click any widget, then look for the Style option (sometimes a tab, sometimes a section within the settings). Here is what you can control:
| Property | What It Does | Common Values |
|---|---|---|
| Background | Fill color or image behind the widget content | Solid color, gradient, or image URL |
| Border | Outline around the widget | 1px solid #ddd, or no border |
| Border Radius | Rounds the corners | 0 (sharp), 8px (subtle), 16px (rounded) |
| Shadow | Drop shadow beneath the widget | None, small, medium, large presets |
| Padding | Space between content and the widget edge | 8px, 16px, 24px |
| Text Color | Overrides theme text color for this widget | Any hex color |
| Font Size | Override theme font size | 14px, 16px, 20px |
The theme is your apartment's paint color. The Style tab is furniture and decor for each room. You might have a white apartment (theme), but the living room gets a dark accent wall (one widget's background override), the bedroom gets soft curtains (another widget's shadow and radius), and the kitchen stays plain. Same palette, different treatments per room.
Select any Text widget. Go to its Style tab. Set the background to your theme's Primary color, text color to white, border-radius to 12px, and add a medium shadow. Congratulations — you just made a styled "card" component. Now do the same thing 4 more times until it is muscle memory.
If you override text color on every single widget, you have defeated the purpose of themes. When you later switch themes, all your manual overrides will remain while the theme changes around them — creating a visual mess. Only override when you have a specific reason. If every widget needs the same custom color, change the theme color instead.
Backgrounds are the biggest visual real estate in your app. A solid white background is fine. A well-chosen background — subtle gradient, muted image, or tinted overlay — is what separates "fine" from "this person knows what they are doing."
Simple, clean, fastest to load. Use for most sections. Off-white (#F5F5F5) beats pure white (#FFFFFF) — less harsh on screens.
Adds depth without complexity. Keep it subtle — 2 colors max, similar hues. Bold gradients scream 2015.
Dramatic but tricky. Must be high-res, low-contrast, and ALWAYS paired with a dark overlay so text remains readable.
/* If you ever need to add a gradient overlay via custom CSS: */ background: linear-gradient( 180deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.2) 100% ); /* Common safe background colors for EB sections: */ --paper-white: #FAFAFA; /* softer than #FFF */ --warm-gray: #F0EDEB; /* adds warmth */ --cool-slate: #E8ECF1; /* modern tech feel */ --dark-navy: #1B2838; /* ops dashboard */
Here is the dirty secret of visual design: spacing does more work than color. An app with perfect colors but terrible spacing looks messy. An app with a boring two-color palette but consistent spacing looks professional. If you only fix one thing, fix your spacing.
Space inside the box, between the content and the border. Like the cushion inside a picture frame. Controls how "breathable" content feels.
Space outside the box, between the widget and its neighbors. Controls how close or far apart widgets sit from each other.
Padding is the cushion inside your couch. Margin is the space between your couch and the wall. Both matter. A couch jammed against the wall with flat cushions (no padding, no margin) feels claustrophobic. Add padding so the content breathes, then add margin so the widget has room.
| Element | Recommended Padding | Recommended Gap |
|---|---|---|
| Section containers | 24px - 40px | 16px - 24px between sections |
| Cards / content blocks | 16px - 24px | 12px - 16px between cards |
| Buttons | 8px - 12px vertical, 16px - 24px horizontal | 8px between buttons |
| Text in containers | 12px - 16px | 8px between text blocks |
| Page body edges | 24px - 48px | N/A |
Professional designers use multiples of 8 for all spacing: 8px, 16px, 24px, 32px, 40px, 48px. Never use random numbers like 13px or 37px. When every space is a multiple of 8, the whole app feels "aligned" even if users cannot articulate why. This single rule will do more for your visual quality than any color change.
You know the basics. Now let's build apps that look like a design team shipped them, not a GIS analyst on a deadline.
The built-in themes get you 70% of the way there. The other 30% — the part that makes your app feel intentional rather than templated — requires building a custom theme from scratch. EB makes this surprisingly straightforward.
Custom themes are saved to your ArcGIS Online organization. Other builders in your org can see and use them. This makes themes the foundation of organizational design consistency. Build one good theme and every app your team ships will look cohesive.
Build a custom theme using only 3 colors: one primary, one neutral background, and one accent. Constrain yourself deliberately. You will discover that 3-color apps look cleaner than 7-color apps every single time.
Start with one color you love, then derive everything else from it. Take your primary color, desaturate it 60% for backgrounds, lighten it 80% for hover states, darken it 40% for text. Monochromatic palettes (one hue, many tints/shades) always look professional. Random multi-hue palettes almost never do.
If you are building apps for the American Red Cross — or any organization with brand guidelines — "close enough" is not good enough. Brand compliance means exact hex values, correct fonts, proper logo usage, and consistent voice. Here is how to get it right in EB.
| Element | ARC Standard | EB Implementation |
|---|---|---|
| Primary Red | #ED1B2E | Theme Primary color |
| Body Text | #333333 | Theme Dark color |
| Secondary Text | #666666 | Theme text-dim equivalent |
| Background | #FFFFFF or #F5F5F5 | Theme Light color |
| Logo | Red Cross on white, minimum clear space of 1x logo height | Image widget with padding, never stretch or recolor |
| Font | ARC uses proprietary fonts; in digital, use clean sans-serif (Avenir, Helvetica, Arial) | Theme font family set to Avenir Next or closest match |
#FF0000 is not ARC red. #ED1B2E is. The difference is visible and it matters#333333#ED1B2E#333333, Light to #FFFFFF, and a neutral gray (#F5F5F5) for section backgrounds8px on all sides for clear spaceYour app will be used on 27-inch monitors, 13-inch laptops, iPads, and phones. If you only test on your monitor, you are guaranteeing that it looks broken for at least half your users. Responsive styling means your app adapts gracefully to every screen size.
Experience Builder has built-in breakpoints. When you resize the canvas in the builder, you will see indicators for different device sizes. The key breakpoints are:
Design for the smallest screen first, then add complexity for larger screens. It is much easier to add elements as space increases than to cram a desktop design into a phone. For EB specifically: build your stacked mobile layout first, then switch to desktop view and arrange side-by-side layouts.
| Property | Desktop | Tablet | Phone |
|---|---|---|---|
| Layout direction | Row (side by side) | Row or Column | Column (stacked) |
| Font size (body) | 16px | 15px | 14px |
| Font size (heading) | 28px | 24px | 20px |
| Padding | 32px | 24px | 16px |
| Sidebar | Visible, 300px | Collapsible | Hidden or drawer |
width: 400px) will overflow on smaller screens. Use percentages or let EB's layout handle itAccessible styling is not a nice-to-have. If your app is used by the public — or by any government or nonprofit — accessibility is a legal and ethical requirement. The good news: most accessibility fixes are also good design. Accessible apps look better for everyone.
WCAG 2.1 AA requires a 4.5:1 contrast ratio for normal text and 3:1 for large text (18px+ or 14px+ bold).
Body text must be at least 14px. 16px is better. Anything smaller fails readability for aging eyes and mobile screens.
Keyboard users navigate with Tab. Every interactive element must have a visible focus ring — never remove outline styles.
| Combination | Contrast Ratio | WCAG AA? |
|---|---|---|
Black #000 on White #FFF | 21:1 | Pass |
Dark Gray #333 on White #FFF | 12.6:1 | Pass |
ARC Red #ED1B2E on White #FFF | 4.0:1 | Fails for small text (needs 4.5:1) |
Light Gray #999 on White #FFF | 2.8:1 | Fail |
White #FFF on Dark Navy #1B2838 | 14.8:1 | Pass |
ARC red (#ED1B2E) on white fails WCAG AA for small body text. It's close (4.0:1 vs the required 4.5:1) but it fails. Use ARC red for large headings (18px+), buttons with white text, or icons — but never for body paragraphs. Body text should be #333333 on white. This catches a lot of people off guard.
Go to WebAIM Contrast Checker. Type your foreground and background colors. Check every text color in your app. Fix anything below 4.5:1 for body text. This takes 10 minutes and catches the most common accessibility failure in EB apps.
Dark mode is not just an aesthetic choice. For operational apps — EOC dashboards, real-time incident maps, overnight monitoring — dark mode reduces eye strain, saves battery on OLED screens, and makes map data pop. For public-facing apps, light mode is usually better for readability and trust.
| Factor | Light Mode | Dark Mode |
|---|---|---|
| Best for | Public sites, reports, story maps | Dashboards, ops centers, map-heavy apps |
| Readability | Better for long text, familiar to most users | Better for data-dense displays, reduces glare |
| Map appearance | Light basemaps (streets, terrain) | Dark basemaps (dark gray canvas, imagery) |
| Eye strain | Fine for daytime use | Better for extended or nighttime sessions |
| Trust signal | Feels "official," "government," approachable | Feels "operational," "technical," serious |
#000000) as your background. Use dark grays like #1A1A2E or #1B2838. Pure black creates too much contrast and causes halos around white text/* Professional dark mode palette for EB apps */ --bg-primary: #0F1923; /* page background — NOT pure black */ --bg-secondary: #1B2838; /* cards, panels, sidebar */ --bg-tertiary: #243447; /* hover states, active items */ --text-primary: #E8ECF1; /* main text — NOT pure white */ --text-secondary:#8899AA; /* labels, captions, muted text */ --border: #2D3F52; /* subtle dividers */ --accent: #4ECDC4; /* teal accent — pops on dark */
Never use pure black (#000) and pure white (#FFF) together in dark mode. The extreme contrast (21:1) causes "halation" — text appears to vibrate and glow. Use off-black backgrounds (#121212 to #1E1E1E) and off-white text (#E0E0E0 to #F0F0F0). The contrast is still excellent (15:1+) but far more comfortable to read.
Visual hierarchy is the art of directing your user's eyes. When someone opens your app, they should instantly know: what is important, what is clickable, and where to look first. If everything is the same size, weight, and color, nothing is important — and users bounce.
Squint at your app until everything is blurry. The elements that are still visible — the big headline, the colored button, the map — are your visual hierarchy. If when you squint you see a flat, even gray blur with nothing standing out, your hierarchy is broken. Something should always be louder than everything else.
Biggest = most important. Your page title should be visibly larger than section headers, which should be larger than body text. If you cannot tell which is which, increase the difference.
Bright/saturated colors draw the eye before muted ones. Use your accent color sparingly — only on the one thing you most want clicked. If everything is accent-colored, nothing is.
| Element | Role | Styling Approach |
|---|---|---|
| Icons | Quick visual scanning, category identification | Consistent size (24px-32px), consistent style family, muted color unless actionable |
| Hero images | Emotional impact, context setting | Full-width, high quality, with overlay for text readability |
| Thumbnail images | Preview of content, list identification | Consistent aspect ratio, consistent size, border-radius for polish |
| Dividers | Section separation | Subtle (1px, light gray). If dividers are noticeable, they are too heavy |
| White space | Breathing room, group separation | More is almost always better. When in doubt, add more space |
Your app should have exactly three visual levels: (1) the primary action or headline — biggest, boldest, most colorful; (2) secondary content — medium size, neutral colors; (3) supporting details — smallest, most muted. If you have more than 3 levels, combine some. If you have fewer than 3, everything looks flat.
Custom CSS injection, animations, multi-page consistency, and the production patterns that separate senior builders from everyone else.
Sometimes the Theme and Style tab are not enough. You need to change something they do not expose — a specific hover color, a scrollbar style, the opacity of a header. This is where custom CSS comes in. It is powerful, but it is also the most fragile tool in your kit.
EB generates CSS class names dynamically. When Esri ships an update, class names can change without notice. Custom CSS that worked yesterday may break tomorrow. Always use custom CSS as a last resort, and always document what you have customized so you can fix it after updates.
Add an Embed widget, switch to Code mode, and write a <style> block. The CSS applies to the entire page. This is the most common injection method.
Right-click an element, choose Inspect, find the class name in DevTools, then write CSS targeting that class in your Embed widget. Essential for discovering what to target.
<style> tags. The CSS will apply to the entire published page, not just the Embed widget.widget-header) rather than auto-generated hashes (like .css-1a2b3c). Semantic classes are less likely to change between updates<style> /* Hide the default EB header bar */ .header-bar { display: none !important; } /* Custom scrollbar for the whole app */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: #1B2838; } ::-webkit-scrollbar-thumb { background: #3D5A73; border-radius: 3px; } /* Make all card widgets have rounded corners */ .card-widget { border-radius: 12px !important; overflow: hidden; } /* Subtle hover effect on clickable elements */ .widget-card-content:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.2s ease; } </style>
EB applies its own styles with high specificity. You will often need !important to override them. This is normally a CSS anti-pattern, but in EB it is the reality of working inside someone else's framework. Use it deliberately, not as a habit, and always add a comment explaining why.
Animations are the difference between an app that feels static and one that feels alive. But there is a line. Cross it and your app feels like a 2005 Flash website. The key is subtlety: users should feel the animation without consciously noticing it.
Buttons that smoothly change color, cards that lift slightly on hover. Takes 100ms-200ms. Users feel it as "responsive."
Pulse or shimmer effects while data loads. Tells users "something is happening" instead of dead silence.
Smooth color shifts when something is selected, expanded, or toggled. Helps users track what changed and why.
/* Smooth hover lift — the single most impactful micro-interaction */ .widget-card { transition: transform 0.2s ease, box-shadow 0.2s ease; } .widget-card:hover { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(0,0,0,0.12); } /* Smooth button color change */ .jimu-btn { transition: background-color 0.15s ease, color 0.15s ease; } /* Pulse animation for loading indicators */ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .loading-indicator { animation: pulse 1.5s ease-in-out infinite; } /* Smooth border color on focus (accessibility + polish) */ input:focus, select:focus { outline: none; border-color: #4ECDC4; box-shadow: 0 0 0 3px rgba(78,205,196,0.25); transition: border-color 0.15s, box-shadow 0.15s; }
ease or ease-outSome users have vestibular disorders that make animations nauseating. Always respect the prefers-reduced-motion media query. Wrap your animations so they are disabled for users who have enabled "Reduce Motion" in their OS settings:
@media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }
Single-page apps are easy to keep consistent. Multi-page apps are where things fall apart. Page 1 has 16px body text, page 2 has 14px. Page 1 uses 24px padding, page 3 uses 32px. Nobody planned it — it just drifted. Here is how to prevent drift.
Write these down before you start building a multi-page app:
Design tokens are your app's constitution. When you are on page 7 of a 10-page app at 11 PM and you think "should this header be 20px or 22px?", you check the constitution instead of guessing. Without it, every page becomes a little different, and by page 10 your app looks like it was built by 10 different people.
Let's put everything together. Here is a before/after of a typical EB dashboard, showing the exact changes that transform it from "government template" to "polished operational tool."
| Element | Before (Default) | After (Professional) |
|---|---|---|
| Theme | Default Esri blue/white | Custom dark theme (#0F1923 bg) |
| Font | Default (varies) | Avenir Next, 4 size levels |
| Header | Full-width blue bar with white text | Slim dark bar, logo left, nav right, 52px height |
| Sidebar | White panel, default borders | Dark panel (#1B2838), no border, subtle shadow |
| Map | Light gray basemap | Dark gray canvas basemap, matching the theme |
| Cards | White boxes, no radius | Dark cards (#243447), 8px radius, 1px border |
| Data text | All same size, same color | KPI numbers 28px bold, labels 12px muted, clear hierarchy |
| Spacing | Random (10px here, 15px there) | All multiples of 8: 8, 16, 24, 32 |
| Hover states | None | Cards lift 2px with shadow on hover |
| Status colors | Random reds/greens | Semantic: green (#00D68F), amber (#F5A623), red (#FF6B6B) |
/* Complete dashboard polish — inject via Embed widget */ <style> /* Base dark background */ body { background: #0F1923; color: #E8ECF1; } /* KPI number styling */ .kpi-value { font-size: 28px; font-weight: 700; letter-spacing: -0.5px; color: #FFFFFF; } .kpi-label { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: #8899AA; } /* Card hover interaction */ .dashboard-card { background: #1B2838; border: 1px solid #2D3F52; border-radius: 8px; padding: 24px; transition: transform 0.2s, box-shadow 0.2s; } .dashboard-card:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.3); } /* Status colors — consistent semantic meaning */ .status-good { color: #00D68F; } .status-warning { color: #F5A623; } .status-critical{ color: #FF6B6B; } </style>
Take any existing EB app and apply these changes one at a time, in order: (1) Switch to a dark theme. (2) Set all spacing to multiples of 8. (3) Create a 3-level text hierarchy. (4) Add card hover effects via custom CSS. Preview after each step. You will see a dramatic transformation in 15 minutes.
The most beautiful app in the world is useless if it takes 8 seconds to load. Styling choices have real performance costs. Images, fonts, and CSS complexity all add weight to your app. Here is how to stay fast while looking great.
The #1 performance killer. Unoptimized images account for 60-80% of page weight in most EB apps. A 4MB hero image is inexcusable.
Custom fonts add 100-400KB per family. Every additional font slows initial render. Use the theme's built-in fonts when possible.
| Asset | Target Size | Tool |
|---|---|---|
| Hero/background image | Under 300KB | Squoosh — compress to WebP at 80% quality |
| Thumbnail images | Under 50KB each | Resize to actual display dimensions before uploading |
| Logo/icons | Under 20KB | Use SVG when possible — infinitely scalable, tiny file size |
| Custom fonts | 0 extra (use built-in) | Stick with EB's included font list — they are already loaded |
| CSS (via Embed) | Under 5KB | Keep it targeted — only override what you must |
!important will slow renderingIf your app does not become visually usable within 3 seconds, you have already lost a significant portion of users. On mobile networks, this is even more critical. Every 100KB of images adds roughly 0.5 seconds on a typical mobile connection. Optimize images first — it is the single highest-impact performance improvement you can make.