Skip to content

Custom CSS Guide

This guide explains how to use the Custom CSS feature in NotionApps to style a published app for client-facing or brand-specific use cases.

Custom CSS gives app builders a controlled way to customize the visual appearance of the end-user app. You can use it to adjust fonts, text colors, buttons, form fields, links, images, spacing, and responsive behavior without changing the app’s data structure or backend logic.

This guide is intended for:

  • App builders who want to apply brand styling to a NotionApps app.
  • Technical writers documenting NotionApps customization workflows.
  • Customer success teams helping users style client-facing apps.
  • Developers or advanced users who understand basic CSS selectors.

With Custom CSS, you can style many visible parts of the end-user app, including:

  • Text color and typography
  • Headings, labels, body text, and links
  • Buttons and button hover states
  • Inputs, textareas, dropdowns, and placeholders
  • Images and icons
  • Lists and repeated records
  • Borders, spacing, backgrounds, and shadows
  • Mobile-specific layouts using media queries

Custom CSS is best suited for visual customization. It should not be used to control access, hide sensitive data, replace validation logic, or change application behavior.

Before writing Custom CSS, make sure:

  • You have access to the app in the NotionApps builder.
  • The app has been created and can be saved.
  • You know the styling goal, such as applying a brand font, changing button colors, or styling form fields.
  • You understand that users must save and re-publish the app before published users see the latest CSS.

To add Custom CSS:

  1. Open the app in the NotionApps builder.
  2. Go to the app settings area.
  3. Find the Custom CSS section.
  4. Enable Custom CSS.
  5. Paste your CSS into the Custom CSS editor.
  1. Save the app.
  2. Publish or re-publish the app.
  3. Open the published app and verify the style changes.

The NotionApps end-user app is wrapped in a root element with this attribute:

data-notionapps-end-user-root="true"

When writing Custom CSS, scope your selectors to that root element:

[data-notionapps-end-user-root="true"] {
color: #111827;
}

This is the recommended pattern because it limits your CSS to the NotionApps end-user app.

Scoped CSS is safer than global CSS.

For example, this selector targets every button inside the NotionApps end-user app:

[data-notionapps-end-user-root="true"] button {
border-radius: 6px;
}

This selector targets every button on the page:

button {
border-radius: 6px;
}

The second example is broader and can affect elements outside the app. Use scoped selectors whenever possible.

CSS variables make brand styles easier to maintain. Define them once on the root selector:

[data-notionapps-end-user-root="true"] {
--brand-primary: #0f766e;
--brand-primary-dark: #115e59;
--brand-border: #d8ebe7;
--brand-text: #111827;
--brand-muted-text: #64748b;
color: var(--brand-text);
font-family: "Poppins", sans-serif;
}

You can then reuse those values:

[data-notionapps-end-user-root="true"] button {
background: var(--brand-primary);
}

To change the color of all text inside the end-user app, target both the root and all child elements:

[data-notionapps-end-user-root="true"],
[data-notionapps-end-user-root="true"] * {
color: red !important;
}

The universal selector * targets every descendant element.

Use this approach when you want a global text color or when testing that Custom CSS is working.

Some browsers apply special rendering to form controls. If the text inside inputs, dropdowns, or textareas does not change color, add -webkit-text-fill-color.

[data-notionapps-end-user-root="true"] input,
[data-notionapps-end-user-root="true"] textarea,
[data-notionapps-end-user-root="true"] select {
color: red !important;
-webkit-text-fill-color: red !important;
}

Placeholder text requires a separate selector:

[data-notionapps-end-user-root="true"] input::placeholder,
[data-notionapps-end-user-root="true"] textarea::placeholder {
color: #94a3b8;
opacity: 1;
}

Use opacity: 1 because some browsers reduce placeholder opacity by default.

Use heading selectors to style screen titles, section titles, or large text elements.

[data-notionapps-end-user-root="true"] h1,
[data-notionapps-end-user-root="true"] h2,
[data-notionapps-end-user-root="true"] h3 {
color: #0f172a;
font-weight: 700;
letter-spacing: 0;
}

You can style heading levels separately:

[data-notionapps-end-user-root="true"] h1 {
font-size: 32px;
}
[data-notionapps-end-user-root="true"] h2 {
font-size: 24px;
}
[data-notionapps-end-user-root="true"] h3 {
font-size: 18px;
}

Use these selectors for common text elements:

[data-notionapps-end-user-root="true"] p,
[data-notionapps-end-user-root="true"] span,
[data-notionapps-end-user-root="true"] label {
color: #334155;
}

If a label does not change, inspect the element in browser DevTools. It may be rendered as a div, span, or another element.

Use the a selector for links:

[data-notionapps-end-user-root="true"] a {
color: var(--brand-primary);
font-weight: 500;
text-decoration: underline;
}
[data-notionapps-end-user-root="true"] a:hover {
color: var(--brand-primary-dark);
}

Buttons may be rendered as actual button elements or as elements with role="button". Target both:

[data-notionapps-end-user-root="true"] button,
[data-notionapps-end-user-root="true"] [role="button"] {
background: var(--brand-primary) !important;
border-color: var(--brand-primary) !important;
border-radius: 6px;
color: #ffffff !important;
font-weight: 600;
}

Add a hover state:

[data-notionapps-end-user-root="true"] button:hover,
[data-notionapps-end-user-root="true"] [role="button"]:hover {
background: var(--brand-primary-dark) !important;
border-color: var(--brand-primary-dark) !important;
}

Add a disabled state:

[data-notionapps-end-user-root="true"] button:disabled {
background: #cbd5e1 !important;
border-color: #cbd5e1 !important;
color: #64748b !important;
cursor: not-allowed;
}

Use these selectors for common form fields:

[data-notionapps-end-user-root="true"] input,
[data-notionapps-end-user-root="true"] textarea,
[data-notionapps-end-user-root="true"] select {
background: #ffffff;
border: 1px solid var(--brand-border) !important;
border-radius: 6px;
color: #111827;
font-family: "Poppins", sans-serif;
}

Add focus styling:

[data-notionapps-end-user-root="true"] input:focus,
[data-notionapps-end-user-root="true"] textarea:focus,
[data-notionapps-end-user-root="true"] select:focus {
border-color: var(--brand-primary) !important;
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.18);
outline: none;
}

Use list selectors for unordered and ordered lists:

[data-notionapps-end-user-root="true"] ul,
[data-notionapps-end-user-root="true"] ol {
padding-left: 20px;
}
[data-notionapps-end-user-root="true"] li {
margin-bottom: 8px;
}

Use image selectors for rounded corners or consistent sizing:

[data-notionapps-end-user-root="true"] img {
border-radius: 6px;
max-width: 100%;
}

If images are cropped or displayed in cards, test the change on multiple screen sizes.

Icons are often rendered as SVGs. You can target SVG elements:

[data-notionapps-end-user-root="true"] svg {
color: var(--brand-primary);
}

Some SVGs use path elements:

[data-notionapps-end-user-root="true"] svg path {
stroke: currentColor;
}

Use currentColor when possible so icons inherit the surrounding text color.

Many app layouts are built with div elements. You can target them, but use broad div selectors carefully.

[data-notionapps-end-user-root="true"] div {
border-color: #e5e7eb;
}

This rule applies to every div inside the app. For layout styling, inspect the element first and look for a more specific selector when possible.

Attribute selectors target elements by attributes.

The root selector is an attribute selector:

[data-notionapps-end-user-root="true"] {
color: #111827;
}

Button-like elements can be targeted by role:

[data-notionapps-end-user-root="true"] [role="button"] {
cursor: pointer;
}

You can also target input types:

[data-notionapps-end-user-root="true"] input[type="text"],
[data-notionapps-end-user-root="true"] input[type="email"],
[data-notionapps-end-user-root="true"] input[type="number"] {
border-radius: 6px;
}

Pseudo-classes target states such as hover, focus, disabled, or checked.

[data-notionapps-end-user-root="true"] button:hover {
background: var(--brand-primary-dark) !important;
}
[data-notionapps-end-user-root="true"] input:focus {
outline: 2px solid var(--brand-primary);
}
[data-notionapps-end-user-root="true"] button:disabled {
opacity: 0.6;
}

Pseudo-elements target parts of an element, such as placeholders.

[data-notionapps-end-user-root="true"] input::placeholder {
color: #94a3b8;
}

Media queries let you apply styles only at certain viewport widths.

Example for mobile:

@media (max-width: 600px) {
[data-notionapps-end-user-root="true"] button {
min-height: 44px;
width: 100%;
}
[data-notionapps-end-user-root="true"] h1 {
font-size: 24px;
}
}

Example for larger screens:

@media (min-width: 900px) {
[data-notionapps-end-user-root="true"] {
font-size: 16px;
}
}

If the app also uses the Custom Font feature, set the same font family in Custom CSS:

[data-notionapps-end-user-root="true"],
[data-notionapps-end-user-root="true"] * {
font-family: "Poppins", sans-serif;
}

If a font is loaded from Google Fonts, make sure the font family name in CSS matches the font name exactly.

Example:

Font family: Poppins
Stylesheet URL: https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap

CSS:

[data-notionapps-end-user-root="true"] {
font-family: "Poppins", sans-serif;
}

Use this example to verify that Custom CSS is working:

[data-notionapps-end-user-root="true"],
[data-notionapps-end-user-root="true"] * {
color: red !important;
}
[data-notionapps-end-user-root="true"] input,
[data-notionapps-end-user-root="true"] textarea,
[data-notionapps-end-user-root="true"] select,
[data-notionapps-end-user-root="true"] button {
color: red !important;
-webkit-text-fill-color: red !important;
}
[data-notionapps-end-user-root="true"] input::placeholder,
[data-notionapps-end-user-root="true"] textarea::placeholder {
color: red !important;
opacity: 1;
}

After saving and publishing, all visible text inside the end-user app should appear red.

This example applies a client brand color to buttons and form controls:

[data-notionapps-end-user-root="true"] {
--brand-primary: #0f766e;
--brand-primary-dark: #115e59;
--brand-border: #d8ebe7;
--brand-text: #111827;
color: var(--brand-text);
font-family: "Poppins", sans-serif;
}
[data-notionapps-end-user-root="true"] button,
[data-notionapps-end-user-root="true"] [role="button"] {
background: var(--brand-primary) !important;
border-color: var(--brand-primary) !important;
border-radius: 6px;
color: #ffffff !important;
font-weight: 600;
}
[data-notionapps-end-user-root="true"] button:hover,
[data-notionapps-end-user-root="true"] [role="button"]:hover {
background: var(--brand-primary-dark) !important;
border-color: var(--brand-primary-dark) !important;
}
[data-notionapps-end-user-root="true"] input,
[data-notionapps-end-user-root="true"] textarea,
[data-notionapps-end-user-root="true"] select {
border-color: var(--brand-border) !important;
border-radius: 6px;
font-family: "Poppins", sans-serif;
}

This example makes form fields feel lighter and more spacious:

[data-notionapps-end-user-root="true"] input,
[data-notionapps-end-user-root="true"] textarea,
[data-notionapps-end-user-root="true"] select {
background: #f8fafc;
border: 1px solid #cbd5e1 !important;
border-radius: 6px;
padding: 10px 12px;
}
[data-notionapps-end-user-root="true"] input:focus,
[data-notionapps-end-user-root="true"] textarea:focus,
[data-notionapps-end-user-root="true"] select:focus {
background: #ffffff;
border-color: #0f766e !important;
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.16);
}

This example makes buttons easier to tap on mobile screens:

@media (max-width: 600px) {
[data-notionapps-end-user-root="true"] button,
[data-notionapps-end-user-root="true"] [role="button"] {
min-height: 44px;
width: 100%;
}
}

After saving and publishing, open the end-user app and inspect it with browser DevTools.

Check for the root element:

data-notionapps-end-user-root="true"

Check for the custom style tag:

<style id="notionapps-end-user-customization">

If that style tag appears, the published app received the Custom CSS payload.

Confirm the following:

  • Custom CSS is enabled.
  • The app was saved after editing the CSS.
  • The app was re-published.
  • The published app was hard-refreshed.
  • The page contains id="notionapps-end-user-customization".

The existing component style may be more specific than your selector. Increase specificity:

[data-notionapps-end-user-root="true"] button {
background: #0f766e !important;
}

Use !important only when needed.

Add -webkit-text-fill-color:

[data-notionapps-end-user-root="true"] input {
color: red !important;
-webkit-text-fill-color: red !important;
}

Target placeholders separately:

[data-notionapps-end-user-root="true"] input::placeholder {
color: red !important;
opacity: 1;
}

Add mobile-specific media queries:

@media (max-width: 600px) {
[data-notionapps-end-user-root="true"] {
font-size: 15px;
}
}

If a rule affects more than expected, make it narrower.

Broad:

[data-notionapps-end-user-root="true"] div {
padding: 12px;
}

Narrower:

[data-notionapps-end-user-root="true"] form div {
padding: 12px;
}
  1. Define the brand variables first.
  2. Add typography styles.
  3. Style buttons.
  4. Style form fields.
  5. Add hover, focus, and disabled states.
  6. Add mobile media queries.
  7. Save and publish.
  8. Verify in the published end-user app.
  9. Inspect problem elements with DevTools.
  10. Refine selectors if a rule is too broad.
Goal Selector
App root [data-notionapps-end-user-root="true"]
All elements in app [data-notionapps-end-user-root="true"] *
Headings [data-notionapps-end-user-root="true"] h1, h2, h3
Paragraphs [data-notionapps-end-user-root="true"] p
Labels [data-notionapps-end-user-root="true"] label
Links [data-notionapps-end-user-root="true"] a
Buttons [data-notionapps-end-user-root="true"] button
Button-like elements [data-notionapps-end-user-root="true"] [role="button"]
Inputs [data-notionapps-end-user-root="true"] input
Textareas [data-notionapps-end-user-root="true"] textarea
Dropdowns [data-notionapps-end-user-root="true"] select
Placeholders [data-notionapps-end-user-root="true"] input::placeholder
Images [data-notionapps-end-user-root="true"] img
SVG icons [data-notionapps-end-user-root="true"] svg
  • Scope Custom CSS to [data-notionapps-end-user-root="true"].
  • Use CSS variables for repeated brand values.
  • Target both button and [role="button"].
  • Target placeholders separately.
  • Use focus states for form accessibility.
  • Use !important only when needed to override existing component styles.
  • Avoid broad layout changes on every div.
  • Test on both desktop and mobile.
  • Save and re-publish after every meaningful change.

Custom CSS in NotionApps is a flexible way to make published apps feel more polished and on-brand. The key is to write scoped, intentional CSS that targets the end-user app root. Start with broad proof-of-concept styles when testing, then refine the selectors for production-ready styling.

This guide teaches a focused maker workflow. For the full screen option inventory (What it does / How to use / Why) and annotated builder screenshots, use the matching Types of Screens guide:

  • Change one option family at a time, then publish and test on phone and desktop.
  • Prefer screen-level settings in Content / Behaviour / Appearance before custom CSS/JS.
  • Keep public access and navigation visibility intentional on every screen you link from this guide.