BuildBaseBuildBase

Customize the sign-in page

Name your app on the hosted sign-in page, and set its logo, colours, layout, links, words in every language, and stylesheet, per org or per app.

The hosted sign-in page is the first screen of your product a new customer sees, and it should say whose it is. Everything on it is set under User Management → Access → Authentication → Sign-in page, with a preview beside the settings that follows every change before you save. Your app's code changes for none of it, beyond one optional line for the language.

Step by step, with screenshots: Customize the sign-in page.

Whose page it is

The page names your app, in its heading ("Sign in to Acme"), in the browser tab ("Sign in to Acme", or "Forgot your password? · Acme" where the title does not name it) and as the logo's text for screen readers. It never says BuildBase, except in the small "Secured by" badge, which a plan with white-label can hide.

SettingWhere it shows
NameThe {app} in screen titles, people's authenticator apps, the heading of their backup codes, the wallet message, and {{appName}} in emails
Small nameThe browser tab. Empty uses Name
LogoAbove the title. A second logo for dark mode is optional. Emails can show it with {{logoUrl}}
FaviconThe browser tab's icon. Empty uses the logo
WebsiteWhere the logo links

With no Name set, the page uses your organization's name. With neither, it names no app at all ("Sign in"), rather than ours. Emails follow the same Name unless you set the App Name merge tag yourself, under Email → Merge tags; Templates and merge tags has the details. The page an emailed sign-in link opens carries your branding too, even in a browser other than the one that asked for the link.

One org, several apps. Each client can have its own display name, logo, dark logo, favicon and brand colour, in the client's Sign-in page section (open the client on the Settings tab). A sign-in started by that client's app shows them, and so do the consent and device screens that name the app. So do the emails that sign-in sends (the code or link, the welcome, the sign-in notices): they name that app and carry its logo. Anything left empty falls back to the org's settings. The client's console title is a label for you and is not shown to the people signing in, except for an agent that registered itself, whose own name is all there is.

Words on the page

Every sentence on the page that you can write is in one place, Words on the page: each screen's title and the line under it, the waitlist's two screens, the sign-up consent checkbox, and the split screen panel's headline and line. Ours name your app and are already in every language the page speaks, so most apps never change them. Buttons, field labels and errors are always ours, in every language.

Write {app} where the app's name belongs: Sign in to {app} reads "Sign in to Acme Analytics" for that client and "Sign in to Acme" for the others. A box left empty shows ours, greyed in the box; clearing a box is how you go back to ours.

For one app. Pick an app beside Editing to give it its own words, for when its sign-in is for different people or a different job, such as a staff app beside a customer app. A box you leave empty keeps the organization's. The app's words are saved with its client, by the same Save. The client's own Sign-in page section says whether it has words of its own and links here.

In another language. Pick a language beside in to write your words in it. Each box shows the default-language sentence above it, and Start from the English copies it in to edit. Our words are already translated, so write a language only where you changed ours. Nothing is machine translated: every sentence in another language is one you wrote.

Which sentence a visitor reads. For a visitor signing in to an app and reading a language, each sentence is the first of: the app's words in that language; the app's words; the organization's words in that language; the organization's words; ours, in that language. An app's own words come first even over the organization's translation, and the editor warns when that leaves a language reading English.

A language with no version of your words. One setting decides what its visitors read: your words as written (the default, so no page changes until you choose), or ours in their language, so the whole page speaks theirs. The editor shows what a visitor would read under each before you pick.

Out of date. A translation remembers the sentence it was written from. When that sentence changes, the translation is marked May be out of date, with the old and new side by side, until you edit it or choose Mark as checked. The page keeps showing it meanwhile.

Where your words show is a table of every sentence in every language, and what each one reads: yours, ours, the organization's, not translated, or may be out of date. Choose a cell to edit that sentence.

The waitlist's words show only while sign-up is in waitlist mode, and the consent wording only on the sign-up page of an app whose consent checkbox is on; the editor says when nobody sees them.

Brand

FieldWhat it sets
Brand colourButtons, links and focus rings, as #RRGGBB. Button text is black or white, whichever reads better on it
Page backgroundThe page behind the sign-in card, as #RRGGBB. Empty follows the light or dark theme
Background imageAn image behind the card: filling the page, or repeated as a pattern for a small tile
CornersSquare, slightly rounded, rounded or very rounded: the card, fields and buttons
Light or darkFollow the device, always light, or always dark
FontInter (the page's own), the device's own font, a named Google font, or a font file of your own

Leave a field empty to keep the page's own look for it. Changes show the next time a sign-in page is opened.

Contrast. Buttons use your brand colour exactly as you set it. Links, focus rings and other text drawn in it use a shade of it that is readable on each theme: a navy brand is lightened for dark mode, a yellow one darkened for light mode. The settings warn when the colour itself reads poorly, and the preview shows both themes. With no brand colour the page uses a neutral one, near black in light mode and near white in dark.

Fonts. The named fonts are Roboto, Open Sans, Lato, Montserrat, Poppins, Nunito, Work Sans, DM Sans, IBM Plex Sans, Source Sans 3, Merriweather and Lora. They load from Google Fonts on the sign-in page, so the visitor's browser fetches them from Google. A font of your own is a name and an https link to a .woff2 file, and the server holding the file has to let the sign-in page fetch it.

Layout

SettingChoicesDefault
Card positionCentre, left or right of the page, on screens wider than a phoneCentre
Card styleShadow, border, or neitherShadow
LogoCentred, at the start, or hiddenCentred
Logo sizeSmall (32px), medium (48px) or large (80px)Large
Social buttonsAbove or below the email formBelow
Social styleA button per provider with its name, or a row of iconsButtons
HeaderCentred or aligned to the startCentred

A row of icons keeps each provider's name for screen readers.

Split screen. Under Page layout, choose Split screen to put your background image in a panel beside the card: on the left or the right (it mirrors in right-to-left languages), from 1024px wide, with the card centred in the other half. Without a background image the panel is your brand colour. On a phone or a narrow tablet the panel is hidden, or shown as a banner above the card if you prefer. Card position does not apply in a split.

The panel can carry a headline (up to 80 characters) and a line under it (up to 240), such as your product's promise or a customer quote, written under Words on the page like the rest of your words, with a version in each language if you like. They are plain text, at the bottom of the panel over a dark fade so they read on any image. Markup is not accepted: a sign-in page that draws an owner's HTML can be made to look like any other sign-in page.

Help or support, Terms of service and Privacy policy show at the foot of every screen when set, and sign-up names the terms and policy you set. The help link can be a web page or an email address (mailto:[email protected]), and it is also where "lost your authenticator?" and "your account is blocked" point people. Empty hides a link. The people signing in are your users, so our own terms and privacy policy are never shown to them.

Custom CSS

A plan with white-label can add its own stylesheet, under Advanced. It is plain CSS, at most 20,000 characters, and it cannot make the browser fetch anything: the sign-in page holds a password, and a stylesheet that can load a URL can report what a selector matched. So these are refused when you save:

  • url(), image(), image-set(), cross-fade(), element() and src()
  • @import, @font-face and @namespace
  • backslash escapes, which can spell any of the above, and <

Images and fonts have their own settings above. Target these class names, which are kept stable from release to release; the page's generated class names change and are not a contract.

ClassWhat it is
bb-pageThe whole page behind the card
bb-panelA split screen's image panel and its words
bb-panel-titleThe panel's headline
bb-panel-textThe line under the panel's headline
bb-cardThe card that holds every screen
bb-headerThe logo, title and description
bb-logoThe logo image
bb-titleThe screen title
bb-descriptionThe line under the title
bb-alertAn error or notice above the form
bb-contentThe form area
bb-socialThe row of social sign-in buttons
bb-social-buttonOne social sign-in button
bb-footerThe bottom of the card
bb-linksThe help, terms and privacy links
bb-badgeThe "Secured by" badge
bb-languageThe language picker

The page element also carries data-bb-screen with the screen's name, so a rule can apply to one screen: login, register, verify-email, forgot-password, reset-password, check-email, verify-magic-link, email-code, phone-code, phone-signup, wallet-signup, mfa, mfa-setup, trust-device, passkey-enroll, consent, device, device-confirm, beta, error and signout. In a split screen it also carries data-bb-split (start or end) and data-bb-split-phone (hidden or banner), and the card sits inside a main element beside the panel, so write .bb-page .bb-card rather than .bb-page > .bb-card.

[data-bb-screen='register'] .bb-card {
  border: 2px solid hsl(var(--primary));
}
.bb-title {
  letter-spacing: -0.02em;
}

The preview in the console does not apply your stylesheet; open the live page to see it.

What the page tells browsers and search engines

Sign-in pages are not for search results, so every one is sent with noindex, nofollow, as a meta tag and an X-Robots-Tag header. The tab title is the screen's title and your small name (just the title when it already names the app), the icon is your favicon or logo (a neutral padlock when you have neither), and the mobile browser's bar takes the page's background colour. Link previews in chat apps show a plain "Sign in", because they do not run the page's scripts.

The settings are fields on the auth config:

{
  "name": "Acme",
  "smallName": "Acme",
  "supportUrl": "https://acme.com/help",
  "branding": {
    "primaryColor": "#2563EB",
    "backgroundColor": "#F8FAFC",
    "radius": "medium",
    "colorScheme": "system",
    "font": { "family": "Poppins" },
    "layout": { "cardPosition": "start", "socialStyle": "icons" }
  }
}

radius is none, small, medium or large, and colorScheme is system, light or dark. font.family is a name from the list above, inter, system, or custom with name and url. The layout keys are cardPosition (center, start, end), cardStyle (elevated, bordered, flat), logoPosition (center, start, hidden), logoSize (small, medium, large), socialPlacement (top, bottom), socialStyle (buttons, icons), headerAlign (center, start), split (none, start, end) and splitOnPhone (hidden, banner); beside them, branding.backgroundFit is cover or pattern, and branding.panel holds title and text. Every field is optional.

Language

The sign-in pages speak 8 languages, the same ones as the SDK: en, es, fr, de, ja, zh, hi, ar. Arabic is laid out right to left. The page picks the first of these that names a language it speaks:

  1. Your app's language. The SDK sends it when signIn() starts the sign-in: auth.locale on the provider, or else the provider's own locale. A regional tag is narrowed, so es-MX opens the Spanish page.
  2. The visitor's choice from the language switcher in the page's footer, kept for that tab.
  3. Their browser's languages, in their order.
  4. Your default language, from the Language card. It is defaultLocale on the auth config.
  5. English.
// In your SaaSOSProvider auth config
auth={{
  clientId: 'your-client-id',
  redirectUrl: '/callback',
  locale: 'fr', // the sign-in page in French, whatever the SDK's own locale
  callbacks,
}}

Without auth.locale, the provider's locale prop is used, so an app already set to Spanish gets a Spanish sign-in page with no change. An SDK version without auth.locale (0.0.89 and earlier) sends no language, and the page goes by the visitor's choice and their browser. Starting sign-ins without the provider, pass locale to AuthApi.requestAuth(), which sends it in the body of POST /api/v1/auth/request; the page then opens with ?lang= set.

Your own words, in each language. Words you wrote show in a language you gave them a version in, and otherwise as you wrote them, or as ours in that language if you chose that under Words on the page. Words still on ours are translated. A refusal from the server is shown in the page's language when the page knows its reason, and as the server sent it otherwise.

Emails are in English. Codes, magic links and every other email go out in English whatever language the page showed.

Next steps