0%

The Complete Guide to
Themes & Styling

From default Esri blue to polished, branded, accessible apps. Every styling tool EB gives you, explained from the ground up.

Beginner — 7 lessons Intermediate — 6 lessons Advanced — 5 lessons

What's inside

Styling Fundamentals

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.

Lesson 0

Why styling matters

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.

Think of it this way

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.

Theme
Global defaults
Widget Style
Per-widget overrides
Custom CSS
Surgical precision
🎨

Theme Layer

Sets colors, fonts, and base styles for the entire app. Change one setting, every widget updates. This is your foundation.

Widget Style Tab

Override theme defaults for individual widgets. Add backgrounds, borders, shadows, and border-radius per widget.

🔧

Custom CSS

Write raw CSS to target specific elements. The nuclear option — powerful but fragile across EB updates.

LayerScopeSkill NeededStability
ThemeEntire appNone (UI only)Fully supported
Widget Style tabOne widgetNone (UI only)Fully supported
Custom CSSAny elementCSS knowledgeMay break on EB updates
Key takeaway

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.

Lesson 1

Choosing and applying a theme

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.

1
In the EB builder, look at the left toolbar and click the paintbrush icon (the Theme panel)
2
You will see a grid of built-in themes. Hover over each one and your entire app previews in that theme instantly in the canvas
3
Click a theme to apply it. The preview becomes permanent
4
After selecting, the panel expands to show Theme Colors (primary, secondary, etc.) and Font Family options that you can customize
Built-in themes

EB ships with about 12 built-in themes. They fall into two categories:

  • Light themes — white/light gray backgrounds, dark text. Best for public-facing informational apps
  • Dark themes — dark backgrounds, light text. Best for operational dashboards, emergency ops, map-heavy apps
Common mistake

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.

Try it yourself

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.

Lesson 2

Theme colors

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 RoleWhere It AppearsTypical Use
PrimaryHeaders, nav bars, active tabs, primary buttonsYour brand's main color or the dominant UI tone
SecondarySecondary buttons, tags, badges, highlightsComplements primary — should contrast but not clash
LightPage backgrounds, card backgroundsThe "paper" your content sits on
DarkText, header bars, footersThe "ink" — high contrast against Light
Info / AccentLinks, interactive highlights, selected statesDraws attention to interactive elements
SuccessPositive status, complete indicatorsGreen family — signals "all good"
WarningCaution messages, attention-needed statesOrange/yellow family — signals "look here"
DangerError states, destructive actionsRed family — signals "something is wrong"
Think of it this way

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.

1
Open the Theme panel (paintbrush icon) and click on your active theme
2
Click any color swatch to open the color picker. You can type a hex code directly (e.g., #2D5F8A) or use the visual picker
3
Watch the canvas — your change applies everywhere that color role is used. Change Primary and every header, button, and accent updates instantly
4
Pro move: write down your hex codes somewhere. You will need them for the Style tab overrides later
The 60-30-10 rule

60% 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.

Quick check: You want to change the color of ALL headers and nav bars in your app at once. Where do you change it?

Lesson 3

Typography

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.

H

Heading Font

Used for widget titles, section headers, and large text. Choose something with personality and good weight variation.

Aa

Body Font

Used for paragraphs, labels, and smaller text. Prioritize readability over style. Sans-serif is almost always the right call.

FontBest ForPersonality
Avenir NextGeneral purpose, dashboardsClean, modern, professional. Esri's default for good reason
Noto SansInternational apps, multilingualNeutral, supports massive character sets
RobotoData-heavy appsCompact, efficient, "Google-ish"
MerriweatherStory maps, narrative appsElegant serif, feels like a magazine
OswaldBold headers onlyTall, condensed, grabs attention. Do NOT use for body text
Font crimes
  • More than 2 font families: One for headings, one for body. That's it. Three fonts looks like a ransom note
  • Decorative body text: Script, handwriting, or novelty fonts for paragraphs are unreadable
  • All caps body text: Headings can be uppercase. Body text in all caps is genuinely painful to read
  • Under 14px for body: If users have to lean in or zoom in, your font is too small
1
In the Theme panel, scroll to Font Family
2
Click the dropdown and browse available fonts. EB shows a preview of each font in the dropdown
3
The font you pick here applies to all widgets unless a widget overrides it in its own Style tab
4
For heading/body contrast, you will need to set heading fonts at the widget level — the theme sets one global font family
Typography hierarchy

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.

Lesson 4

Widget-level styling

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:

PropertyWhat It DoesCommon Values
BackgroundFill color or image behind the widget contentSolid color, gradient, or image URL
BorderOutline around the widget1px solid #ddd, or no border
Border RadiusRounds the corners0 (sharp), 8px (subtle), 16px (rounded)
ShadowDrop shadow beneath the widgetNone, small, medium, large presets
PaddingSpace between content and the widget edge8px, 16px, 24px
Text ColorOverrides theme text color for this widgetAny hex color
Font SizeOverride theme font size14px, 16px, 20px
Think of it this way

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.

Try it yourself

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.

The override trap

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.

Lesson 5

Background images, colors, and gradients

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."

Solid Color

Simple, clean, fastest to load. Use for most sections. Off-white (#F5F5F5) beats pure white (#FFFFFF) — less harsh on screens.

Gradient

Adds depth without complexity. Keep it subtle — 2 colors max, similar hues. Bold gradients scream 2015.

📷

Background Image

Dramatic but tricky. Must be high-res, low-contrast, and ALWAYS paired with a dark overlay so text remains readable.

1
Click the page body (or a Section/Column widget) to select the container you want to style
2
In the Style tab, find the Background section
3
For solid color: click the color swatch and pick or type a hex code
4
For gradient: some containers support gradient backgrounds. Set two colors and choose linear or radial direction
5
For background image: upload an image or paste a URL. Set it to Cover so it fills the container, and choose Center alignment
subtle-gradient-overlay.css
/* 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 */
Background image mistakes
  • No overlay: Text over a busy photo is unreadable. Always add a semi-transparent dark or light overlay
  • Low-res images: Blurry backgrounds scream amateur. Use 1920x1080 minimum
  • Competing with content: The background is a backdrop, not a focal point. Blur it, darken it, or desaturate it
  • Huge file size: A 5MB background image means your app takes 8 seconds to load. Optimize to under 300KB
Lesson 6

Spacing and alignment

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.

Padding

Space inside the box, between the content and the border. Like the cushion inside a picture frame. Controls how "breathable" content feels.

Margin / Gap

Space outside the box, between the widget and its neighbors. Controls how close or far apart widgets sit from each other.

Think of it this way

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.

ElementRecommended PaddingRecommended Gap
Section containers24px - 40px16px - 24px between sections
Cards / content blocks16px - 24px12px - 16px between cards
Buttons8px - 12px vertical, 16px - 24px horizontal8px between buttons
Text in containers12px - 16px8px between text blocks
Page body edges24px - 48pxN/A
1
In a Row or Column layout, look for the Gap setting — this controls the space between child widgets uniformly. Use it instead of setting margins on individual children
2
For widget-level padding, check the Style tab. Many widgets expose padding controls. If not, wrap the widget in a Section/Column and add padding there
3
The Layout panel (on the left sidebar) shows widget arrangement. Use it to visually verify spacing is consistent
4
If something looks "weird" but you cannot figure out why, it is almost always inconsistent spacing. Make all gaps the same size within a section
The 8px grid rule

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.

Quick check: Your widgets feel "cramped." What should you increase?

Professional Polish

You know the basics. Now let's build apps that look like a design team shipped them, not a GIS analyst on a deadline.

Lesson 7

Custom themes

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.

1
Open the Theme panel and click Custom theme at the bottom of the theme grid
2
Start with the base mode: choose Light or Dark. This determines the default background and text contrast direction
3
Set your Primary color first. Every other color should relate to it. If your primary is a deep blue, your secondary might be a muted teal, not a random orange
4
Set Secondary, Accent, and neutral colors. Use a tool like coolors.co or Adobe Color to generate harmonious palettes
5
Pick your font family. One is usually enough — you get visual distinction through weight (bold vs regular) and size, not multiple fonts
6
Click Save. Your custom theme appears in the theme gallery for reuse across apps in your organization
Theme sharing

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.

Try it yourself

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.

The palette strategy

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.

Lesson 8

Brand compliance

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.

#ED1B2E
#333333
#666666
#FFFFFF
#F5F5F5
ElementARC StandardEB Implementation
Primary Red#ED1B2ETheme Primary color
Body Text#333333Theme Dark color
Secondary Text#666666Theme text-dim equivalent
Background#FFFFFF or #F5F5F5Theme Light color
LogoRed Cross on white, minimum clear space of 1x logo heightImage widget with padding, never stretch or recolor
FontARC uses proprietary fonts; in digital, use clean sans-serif (Avenir, Helvetica, Arial)Theme font family set to Avenir Next or closest match
Brand mistakes that will get you a phone call
  • Wrong red: #FF0000 is not ARC red. #ED1B2E is. The difference is visible and it matters
  • Logo on a red background: The Red Cross logo should always be on white or very light backgrounds with clear space around it
  • Stretched logo: Always constrain proportions. If the cross looks like an oval, fix it
  • Red text on white for body copy: Red is for accents and headers, not body text. Body text is #333333
  • Mixing brand colors with theme defaults: If you use ARC red as primary but leave all other colors as Esri blue defaults, the app looks confused
1
Create a custom theme. Set Primary to #ED1B2E
2
Set Dark to #333333, Light to #FFFFFF, and a neutral gray (#F5F5F5) for section backgrounds
3
Set the font to Avenir Next (Esri includes it). If unavailable, use Helvetica Neue or a clean sans-serif
4
Add the logo as an Image widget in a header Section. Set the Image widget padding to at least 8px on all sides for clear space
5
Save this as a custom theme named something like "ARC Brand 2025" so your team can reuse it

Quick check: What is the correct hex code for American Red Cross red?

Lesson 9

Responsive styling

Your 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.

EB's breakpoint system

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:

  • Large (desktop): 1280px and above — your default design target
  • Medium (tablet): 768px - 1279px — sidebar layouts may need to stack
  • Small (phone): below 768px — everything must stack vertically
1
In the builder, click the device preview buttons in the top toolbar to toggle between desktop, tablet, and phone views
2
When you select a smaller breakpoint, changes you make only apply to that breakpoint and smaller. Larger breakpoints keep their original settings
3
For each widget, check: Does the text overflow? Is the font too large for a phone screen? Do side-by-side layouts still work?
4
Use the responsive layout settings to switch Row layouts to Column at smaller breakpoints, so horizontal content stacks vertically
5
Consider hiding widgets at small breakpoints. That decorative image looks great on desktop but wastes precious phone screen space
Mobile-first mindset

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.

PropertyDesktopTabletPhone
Layout directionRow (side by side)Row or ColumnColumn (stacked)
Font size (body)16px15px14px
Font size (heading)28px24px20px
Padding32px24px16px
SidebarVisible, 300pxCollapsibleHidden or drawer
Responsive traps
  • "I will test on mobile later": You will not. Test at every breakpoint as you build, not as an afterthought
  • Fixed-width elements: Anything set to a pixel width (e.g., width: 400px) will overflow on smaller screens. Use percentages or let EB's layout handle it
  • Hover-dependent interactions: Phones do not have hover. If your styling depends on hover to reveal information, phone users will never see it
Lesson 10

Accessibility and styling

Accessible 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.

👁

Color Contrast

WCAG 2.1 AA requires a 4.5:1 contrast ratio for normal text and 3:1 for large text (18px+ or 14px+ bold).

Aa

Font Size Minimums

Body text must be at least 14px. 16px is better. Anything smaller fails readability for aging eyes and mobile screens.

Focus Indicators

Keyboard users navigate with Tab. Every interactive element must have a visible focus ring — never remove outline styles.

CombinationContrast RatioWCAG AA?
Black #000 on White #FFF21:1Pass
Dark Gray #333 on White #FFF12.6:1Pass
ARC Red #ED1B2E on White #FFF4.0:1Fails for small text (needs 4.5:1)
Light Gray #999 on White #FFF2.8:1Fail
White #FFF on Dark Navy #1B283814.8:1Pass
ARC red accessibility warning

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.

Try it yourself

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.

Lesson 11

Dark mode and light mode

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.

FactorLight ModeDark Mode
Best forPublic sites, reports, story mapsDashboards, ops centers, map-heavy apps
ReadabilityBetter for long text, familiar to most usersBetter for data-dense displays, reduces glare
Map appearanceLight basemaps (streets, terrain)Dark basemaps (dark gray canvas, imagery)
Eye strainFine for daytime useBetter for extended or nighttime sessions
Trust signalFeels "official," "government," approachableFeels "operational," "technical," serious
1
Decide your mode based on who uses the app and when. Public apps for general audiences: light. EOC dashboards watched at 2 AM: dark
2
Pick a theme that matches your mode. Do not try to make a light theme dark by overriding every color — start with a dark theme and customize from there
3
Match your basemap to your mode. A light app with a dark basemap (or vice versa) creates a jarring disconnect. Light mode = light basemap. Dark mode = dark basemap
4
For dark mode, never use pure black (#000000) as your background. Use dark grays like #1A1A2E or #1B2838. Pure black creates too much contrast and causes halos around white text
dark-mode-palette.css
/* 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 */
Dark mode golden rule

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.

Lesson 12

Icons, images, and visual hierarchy

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.

The squint test

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.

📌

Size Creates Hierarchy

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.

🎨

Color Creates Priority

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.

ElementRoleStyling Approach
IconsQuick visual scanning, category identificationConsistent size (24px-32px), consistent style family, muted color unless actionable
Hero imagesEmotional impact, context settingFull-width, high quality, with overlay for text readability
Thumbnail imagesPreview of content, list identificationConsistent aspect ratio, consistent size, border-radius for polish
DividersSection separationSubtle (1px, light gray). If dividers are noticeable, they are too heavy
White spaceBreathing room, group separationMore is almost always better. When in doubt, add more space
1
Pick one icon style and stick with it. Do not mix outline icons with filled icons with emoji. Consistency signals professionalism
2
Make all icons the same size within a section. A 24px icon next to a 32px icon looks like a mistake, even if it was intentional
3
For images in List widgets, set a fixed aspect ratio (e.g., 16:9 or 1:1 square) so every row looks consistent
4
Squint test your app. If nothing stands out, increase the size or color contrast of your most important element by 30%
The 3-level rule

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.

Quick check: You squint at your app and everything looks like a uniform gray blur. What is the most likely problem?

Expert Techniques

Custom CSS injection, animations, multi-page consistency, and the production patterns that separate senior builders from everyone else.

Lesson 13

Custom CSS in Experience Builder

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.

The fragility warning

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.

💉

Embed Widget Method

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.

🔎

Browser DevTools 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.

1
Add an Embed widget to your page. It can be placed anywhere — even off-screen, since it just injects code
2
In the Embed widget settings, switch from URL mode to Code mode
3
Write your CSS inside <style> tags. The CSS will apply to the entire published page, not just the Embed widget
4
To find the right CSS selector, publish your app (or use Preview), right-click the element you want to style, and use your browser's DevTools to find the class name
5
Target classes that look semantic (like .widget-header) rather than auto-generated hashes (like .css-1a2b3c). Semantic classes are less likely to change between updates
embed-widget-css-injection.html
<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>
CSS specificity in EB

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.

Quick check: Why should you prefer semantic class names over auto-generated hashes when writing custom CSS for EB?

Lesson 14

Animation and micro-interactions

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.

👉

Hover Transitions

Buttons that smoothly change color, cards that lift slightly on hover. Takes 100ms-200ms. Users feel it as "responsive."

🔄

Loading States

Pulse or shimmer effects while data loads. Tells users "something is happening" instead of dead silence.

State Changes

Smooth color shifts when something is selected, expanded, or toggled. Helps users track what changed and why.

micro-interactions.css
/* 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;
}
Animation crimes
  • Animations longer than 300ms: Anything over 300ms feels sluggish. Hover transitions should be 100-200ms. Page transitions 200-300ms max
  • Bounce/elastic easing: Elements bouncing like rubber balls look unprofessional in operational tools. Use ease or ease-out
  • Animating everything: If every element animates, nothing feels special and the app feels slow. Animate the 2-3 most important interactions only
  • Animations that block interaction: Users should never have to wait for an animation to finish before they can click something
Accessibility note

Some 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:

reduced-motion.css
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
Lesson 15

Multi-page design consistency

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.

1
Document your design tokens. Before building, write down your font sizes (3-4 sizes), spacing values (multiples of 8), colors (3-5 max), and border-radius (1 value for everything). Keep this list open while you build
2
Build a "master page" first. Get one page exactly right — fonts, spacing, colors, widget styling. Then use it as the visual reference for every other page
3
Use page templates. EB lets you duplicate pages. Build your master, then duplicate and swap content. This is faster and more consistent than building each page from scratch
4
Shared headers and footers. Use the same header/footer Section on every page. If you change the header on one page, change it on all pages. Better yet, use a fixed header layout so it is shared automatically
5
Audit regularly. Every few days, click through every page at every breakpoint. Drift is sneaky — it happens one "quick fix" at a time
The design token checklist

Write these down before you start building a multi-page app:

  • Colors: primary, secondary, accent, background, text (5 colors max)
  • Font sizes: page title, section header, body, caption (4 sizes max)
  • Spacing: page padding, section gap, widget gap, inner padding (4 values, all multiples of 8)
  • Border radius: one value for all rounded corners (8px or 12px — pick one)
  • Shadow: one shadow style for elevated elements (or none)
Think of it this way

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.

Lesson 16

Real-world pattern: professional dashboard styling

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."

ElementBefore (Default)After (Professional)
ThemeDefault Esri blue/whiteCustom dark theme (#0F1923 bg)
FontDefault (varies)Avenir Next, 4 size levels
HeaderFull-width blue bar with white textSlim dark bar, logo left, nav right, 52px height
SidebarWhite panel, default bordersDark panel (#1B2838), no border, subtle shadow
MapLight gray basemapDark gray canvas basemap, matching the theme
CardsWhite boxes, no radiusDark cards (#243447), 8px radius, 1px border
Data textAll same size, same colorKPI numbers 28px bold, labels 12px muted, clear hierarchy
SpacingRandom (10px here, 15px there)All multiples of 8: 8, 16, 24, 32
Hover statesNoneCards lift 2px with shadow on hover
Status colorsRandom reds/greensSemantic: green (#00D68F), amber (#F5A623), red (#FF6B6B)
dashboard-polish.css
/* 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>
Try it yourself

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.

Lesson 17

Performance and styling

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.

📷

Image Optimization

The #1 performance killer. Unoptimized images account for 60-80% of page weight in most EB apps. A 4MB hero image is inexcusable.

AA

Font Loading

Custom fonts add 100-400KB per family. Every additional font slows initial render. Use the theme's built-in fonts when possible.

AssetTarget SizeTool
Hero/background imageUnder 300KBSquoosh — compress to WebP at 80% quality
Thumbnail imagesUnder 50KB eachResize to actual display dimensions before uploading
Logo/iconsUnder 20KBUse SVG when possible — infinitely scalable, tiny file size
Custom fonts0 extra (use built-in)Stick with EB's included font list — they are already loaded
CSS (via Embed)Under 5KBKeep it targeted — only override what you must
1
Audit your images. Right-click each image in your app, check its dimensions and file size. If any image is over 500KB, compress it immediately
2
Resize before uploading. If an image displays at 400x300 but the file is 4000x3000, you are downloading 25x more data than needed. Resize to 800x600 (2x display for retina) maximum
3
Use WebP format. WebP is 25-35% smaller than JPEG at the same quality. Convert with Squoosh — it is free and runs in your browser
4
Minimize custom CSS. Every CSS rule the browser parses adds overhead. 50 targeted rules are fine. 500 rules overriding everything with !important will slow rendering
5
Test on real devices. Your 32GB MacBook loads everything fast. Your user's 5-year-old laptop on hotel WiFi does not. Open Chrome DevTools, go to Network tab, set throttling to "Fast 3G" and reload. That is your real-world performance
The 3-second rule

If 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.

Hidden performance traps
  • Background videos: Just don't. They add 5-20MB of weight and auto-play is blocked on most mobile browsers anyway
  • CSS animations on many elements: Animating 50+ elements simultaneously forces the browser to repaint constantly. Animate only what is visible
  • Multiple Embed widgets with CSS: Each Embed widget adds its own parsing overhead. Consolidate all custom CSS into a single Embed widget
  • Uncompressed PNG screenshots: Screenshots pasted directly from the clipboard are usually PNG at 2-5MB. Convert to JPEG or WebP before using as images in your app

Quick check: Your EB app takes 6 seconds to load. The FIRST thing you should check is: