Branding authn with indigo in a frontend-base world

Hi,

For our site, we have developed customization on top of indigo, so that the login page goes from looking like:

to looking like this:

which is just a few tweaks of the visual demoed here:

https://apps.sandbox.openedx.edly.io/authn/register

For this, we needed this ENV_PATCHES:

("mfe-dockerfile-post-npm-install-authn", "RUN npm install '@edx/brand@github:@edly-io/brand-openedx#ulmo/indigo'")

through a tutor plugin.

I am now trying to theme authn when I activate the frontend_app code with

 apps["authn"]["enabled"] = True

I tried various environment patches, with different branches of the brand-openedx repository:

 ("mfe-dockerfile-post-npm-install-authn", "RUN npm install '@edx/brand@github:@edly-io/brand-openedx#indigo-3.0.0'"),
("mfe-dockerfile-post-npm-install-authn", "RUN npm install '@edx/brand@github:@edly-io/brand-openedx#verawood/indigo'"),

but none of them work.

What would I need to do to apply the Indigo theme to the frontend-base version of authn ? Or is this not yet developed ? Does tutor-indigo support enabling the frontend-base version of authn ? Or does it still only support the MFE version ?

Thanks

The mfe-dockerfile-post-npm-install-authn patch point only works for the legacy Authn MFE. The frontend-base site has an equivalent mfe-dockerfile-post-npm-install-site patch.

It works a bit differently, though, mainly because there is a single frontend-base site for all frontend-base apps. In other words, whatever you npm-install there will be part of the whole dependency tree, not just for that app.

That said, a more efficient way to get the brand package in is to set it so it becomes part of the main npm install step:

hooks.Filters.ENV_PATCHES.add_item(
      (
          "mfe-dockerfile-pre-npm-install-site",
          "RUN npm pkg set
  'dependencies.@edx/brand=npm:@myorg/my-brand-package@^x.y.z'",
      )
  )

Note that in order to support theming of frontend-base sites, the shape of the brand package manifest changed in version 1.3.0. Notably, how the design tokens are exported. See the relevant package.json diff.

As for Indigo, I’m not directly involved with its development, so I don’t know where its frontend-base support ended up, in Verawood. But I did play around with it when building frontend-base into tutor-mfe. I published a modded indigo package (from this branch) that might work (sorry, haven’t tested it in a while) with the frontend-base Authn app if you do:

  hooks.Filters.ENV_PATCHES.add_item(
      (
          "mfe-dockerfile-pre-npm-install-site",
          "RUN npm pkg set
  'dependencies.@edx/brand=npm:@arbrandes/indigo-brand-openedx@^2.6.0'",
      )
  )

Let me know if this helps!

Hi Adolfo,

Your forked package is the only one that got sort of close. Other tutor-indigo/edly-io versions I tried all failed with errors such as

38.11 Module not found: Error: Can't resolve '@edx/brand/light.min.css' in '/openedx/site'
38.11 resolve '@edx/brand/light.min.css' in '/openedx/site'
38.11   Parsed request is a module
38.11   using description file: /openedx/site/package.json (relative path: .)
38.11     Field 'browser' doesn't contain a valid alias configuration
38.11     resolve as module
38.11       looking for modules in /openedx/site/node_modules
38.11         existing directory /openedx/site/node_modules/@edx/brand
38.11           using description file: /openedx/site/node_modules/@edx/brand/package.json (relative path: .)
38.11             using description file: /openedx/site/node_modules/@edx/brand/package.json (relative path: ./light.min.css)
38.11               no extension
38.11                 Field 'browser' doesn't contain a valid alias configuration
38.11                 /openedx/site/node_modules/@edx/brand/light.min.css doesn't exist
38.11               .js
38.11                 Field 'browser' doesn't contain a valid alias configuration
38.11                 /openedx/site/node_modules/@edx/brand/light.min.css.js doesn't exist
38.11               .jsx
38.11                 Field 'browser' doesn't contain a valid alias configuration
38.11                 /openedx/site/node_modules/@edx/brand/light.min.css.jsx doesn't exist
38.11               .ts
38.11                 Field 'browser' doesn't contain a valid alias configuration
38.11                 /openedx/site/node_modules/@edx/brand/light.min.css.ts doesn't exist
38.11               .tsx
38.11                 Field 'browser' doesn't contain a valid alias configuration
38.11                 /openedx/site/node_modules/@edx/brand/light.min.css.tsx doesn't exist
38.11               as directory
38.11                 /openedx/site/node_modules/@edx/brand/light.min.css doesn't exist
38.11       /openedx/node_modules doesn't exist or is not a directory
38.11       /node_modules doesn't exist or is not a directory

Yours didn’t quite get all the way there. The color gradient somehow disappeared, and the “Register” tab showed back. Still easier to figure out, or maybe I will just directly tweak our fork of the authn app. It would be nice if the frontend-base-authn was more customizable with plugin slots. There is only one slot for it, which isn’t so useful.

If I remember correctly, all I did in that fork was tweak the manifest and build. The CSS is the same as before, which would explain it not working with the new HTML in the Authn app. (This also illustrates one of the things that branding authors need to consider when going to frontend-base apps.)

Indeed! We welcome specific ideas and contributions, as usual.

I managed to get authn almost identically themed as my previous one thanks to your

("mfe-dockerfile-pre-npm-install-site", "RUN npm pkg set 'dependencies.@edx/brand=npm:@arbrandes/indigo-brand-openedx@^2.6.0'")

@arbrandes

However, a few surprises came along the way:

  • It seems frontend-base introduces new CSS layers, and it seems that the priority order is reversed, i.e. from top priority to lower priority, we have: paragon, shell, app, site, brand. That seems counter intuitive that the paragon layer has priority over the app layer, which has priority over the site or brand layers. In order to get the styling to apply correctly, I had to introduce CSS code that sits at the paragon layer, i.e.
@layer paragon {
#root .layout {
    background: #052147 !important;
}

#root .layout:before {
    display: none !important;
}

#root .layout .bg-primary-400 h1 .text-accent-a {
    color: #ffffff !important;
    font-family: 'Open Sans' !important;
}
#root .layout .bg-primary-400 h1 img {
    width: 95%;
}

#root .layout .content .div-logo-cq {
    position: relative;
    width: 100%;
    text-align: center;
    margin-top: 2vh;
}
#root .layout .content .div-logo-cq img {
    width: 50%;
    max-width: 300px;
    min-width: 100px;
}

.btn-social {
    width: 100% !important;
}
#root .layout .bg-primary-400 h1 {
    color: var(--pgn-color-primary-base) !important;
    font-size: 32px;
    font-weight: 400;
    line-height: 40px;
    margin: 0 auto !important;
    max-width: 564px !important;
    padding: 50px 0;
    position: relative;
    width: 100%;
    z-index: 1;
}
#root .layout .bg-primary-400 {
    background: none !important;
    display: block;
    flex: 0 0 100%;
    max-width: 100%;
}
}

Note that some of this CSS (the last two items, i.e. #root .layout .bg-primary-400 h1 and #root .layout .bg-primary-400) is code that comes from the app itself, but which does not get applied because the app layer has lower priority than the paragon layer

  • I used to hide registration links with
hooks.Filters.ENV_PATCHES.add_item(("mfe-lms-production-settings", """MFE_CONFIG['SHOW_REGISTRATION_LINKS'] = False"""))

but this no longer works. I tried some combinations of FRONTEND_SITE_CONFIGinstead, such as:

hooks.Filters.ENV_PATCHES.add_item(("mfe-lms-production-settings", """FRONTEND_SITE_CONFIG['commonAppConfig']['SHOW_REGISTRATION_LINKS'] = False"""))

but I could not figure out how to actually pass that setting to the authn app. I ended up hard-coding the value in our fork of the authnapp, i.e.

-  const hideRegistrationLink = useAppConfig().SHOW_REGISTRATION_LINKS === false;
+  const hideRegistrationLink = true; //useAppConfig().SHOW_REGISTRATION_LINKS === false;

That doesn’t sound right. The implemented priority should be, in order of higher priority to lower: anything unlayered > brand > site > app > shell > paragon, as documented here.

No stylesheet should declare its own layer: the layering is done by Webpack itself, based on where the stylesheet is coming from. Where are you introducing this? Any customizations should just be in a brand package - which gets high priority via the brand layer - or just unlayered.

I know it does not, but that’s what happens, at least in my browser (Chrome v150).

0 seems to be the top priority.

I do this in this stylesheet which gets added to all of my pages (through the ExternalScripts handler)

https://edx.evolo.calculquebec.cloud/asset-v1:CQ+evolo101+v1+type@asset+block@calculquebec.css

No, you’re reading this wrong. This is a cascade. The lowest rung wins. (Which is why implicit outer layer is 5: it always wins.)

This means that layer declaration is not doing anything: it comes in too late. Anything in there is always at Layer 5, in practice.

Well that may be the theory, but that’s not the practice I observed.

This is what I observe if I declare these styles with @paragon layer:

which is correct.

This is what I observe if I declare them with any other layer (here, using the @brand layer):

That is also not what I observe. It works fine if I define it with @paragon layer in that file. It very briefly shows incorrectly while it loads, but it gets fixed dynamically within a fraction of a second.

Have you tried not declaring with any layer at all? It should just win.

I have. It does not win. paragon takes over, here is the screenshot:

This is what’s biting you. !important means exactly the opposite in a layer scenario.

Hum, even if I remove the !importantmarkers from my custom style, it still does not show correctly.

I see that the core authn app includes styles that have !important, I guess this is why I’m having issues overwriting them with the custom CSS.

Sadly, Github is down at the moment, but from my local fork, if I go in the release/frontend-basebranch:

[frontend-app-authn]$ git checkout release/frontend-base
Switched to branch 'release/frontend-base'
Your branch is up to date with 'origin/release/frontend-base'.
[frontend-app-authn]$ grep -r important .
./docs/decisions/0002-feature-based-application-organization.rst:**Following the spirit of these principles is more important than following them to the letter.**
./.git/logs/HEAD:7747ca1081a8cab5bb6e44d87b7f3b698c6167ca 383629e433d7760401e247ce8ff181b7ff901886 Maxime Boissonneault <maxime.boissonneault@calculquebec.ca> 1786978023 -0400	commit: remove !important because of new css layers
./.git/logs/refs/heads/cq/verawood-app.dev:7747ca1081a8cab5bb6e44d87b7f3b698c6167ca 383629e433d7760401e247ce8ff181b7ff901886 Maxime Boissonneault <maxime.boissonneault@calculquebec.ca> 1786978023 -0400	commit: remove !important because of new css layers
./.git/hooks/pre-rebase.sample:    Then you can delete it.  More importantly, you should not
./.git/COMMIT_EDITMSG:remove !important because of new css layers
./src/register/messages.jsx:    defaultMessage: 'For account activation and important updates',
./src/style.scss:  padding-top: var(--pgn-spacing-spacer-4) !important;
./src/style.scss:  min-width: 464px !important;
./src/style.scss:  min-width: 464px !important;
./src/style.scss:  padding-bottom: 0px !important;
./src/style.scss:  padding-top: 3px !important;
./src/style.scss:  color: var(--pgn-color-info-300) !important;
./src/style.scss:    color: var(--pgn-color-info-500) !important;
./src/style.scss:  background-color: var(--pgn-color-white) !important;
./src/style.scss:  margin: var(--pgn-spacing-spacer-1) !important;
./src/style.scss:  color: var(--pgn-color-gray-base) !important;
./src/style.scss:  background: none !important;
./src/style.scss:  font-weight: 500 !important;
./src/style.scss:  font-size: var(--pgn-typography-font-size-xs) !important;
./src/style.scss:  filter: drop-shadow($elevation-level-2-shadow) drop-shadow($elevation-level-2-shadow) !important;
./src/style.scss:  right: 0.2rem !important;
./src/style.scss:  font-size: var(--pgn-typography-font-size-xs) !important;
./src/style.scss:  filter: drop-shadow(var(--pgn-elevation-box-shadow-level-2)) drop-shadow(var(--pgn-elevation-box-shadow-level-2)) !important;
./src/style.scss:  bottom: 10px !important;
./src/style.scss:    min-width: 464px !important;
./src/style.scss:  margin-top: 0.188rem !important;
./src/style.scss:    min-width: 464px !important;
./src/style.scss:    padding-top: 1.5rem !important;
./src/style.scss:    margin-right: 0 !important;
./src/style.scss:    min-width: 100vw !important;
./src/style.scss:    padding: 1.5rem !important;
./src/style.scss:      min-width: unset !important;
./src/style.scss:  right: 0 !important;
./src/base-container/components/image-layout/index.scss:    font-size: 60px !important;
./src/base-container/components/image-layout/index.scss:    font-weight: 700 !important;
./src/base-container/components/image-layout/index.scss:    line-height: 60px !important;
./src/base-container/components/image-layout/index.scss:    letter-spacing: -2px !important;
./src/sass/_registration.scss:  margin-bottom: 0 !important;
./src/sass/_registration.scss:    color: var(--pgn-color-primary-base) !important;
./src/sass/_registration.scss:      color: var(--pgn-color-info-700) !important;
./src/sass/_registration.scss:  color: var(--pgn-color-info-500) !important;
./src/sass/_registration.scss:    color: var(--pgn-color-info-700) !important;
./src/sass/_registration.scss:    width: 11rem !important;
./src/sass/_registration.scss:  border: 1px solid var(--danger-300, #CA3A2F) !important;

I guess all of those !important should disappear ?

Most definitely. !important has absolutely no business in a themable app. (It rarely has any business in any properly designed stylesheet.)

Glad we got to the bottom of it. Thanks! :+1:

Also, this is wrong, apologies. What doesn’t work if it comes too late is the layer order (a fact that bit me when initially implementing the layers). It’s perfectly fine for late-loaded stylesheets to declare themselves to be in a particular layer.

That said, once we get rid of all the !important traps in apps themselves (hope Paragon doesn’t have any), the layers should always work as expected.

Ok, but then there are lots of !importanttags which are not under my control. Those are bugs in the apps ?

Even the @paragon layer defines some !important properties. Exploring the properties through the developer tools, I have to disable three styles for the color of the text, to get the white color I want there, because there are 3 such !important declarators, in

  • @edx/brand/dist => core.min.css
    
  • @edx/brand/dist => light.min.css
    
  • paragon => dist/light.min.css
    

Paragon does have some (see my previous post)

Now that we understand the styling gotchas, any idea about this bit ?

Is it because mfe-lms-production-settings is not the right place to add this ? or because it’s the wrong FRONTEND_SITE_CONFIG option to set ?