Keystone-classic: Implement a way to customise / replace the Admin UI Theme

Created on 11 Mar 2014  ·  29Comments  ·  Source: keystonejs/keystone-classic

The Admin UI Theme was designed to be either extended or replaced using LESS, and defaults to vanilla Bootstrap styling.

Let's expose a way for projects to add in their own less files to customise the UI, or a way to provide a complete theme.

Most helpful comment

Here is our working code altered from what @lazamar wrote to use custom styling for the admin UI if this helps anyone!

// Load UI template files
const fs = require('fs');
const buff = fs.readFileSync('node_modules/keystone/templates/layout/base.jade');
const content = buff.toString();

const styleLink = 'link(rel="stylesheet", href="/styles/admin.css")';

// If it already links to our stylesheet we have nothing else to do
if (content.includes(styleLink)) {
    console.log("Admin CSS already applied.");
    return;
}

// Add link to our stylesheet at the end of <head>
const newContent = content.replace('block css', `${styleLink}\n\t\tblock css`);
fs.writeFileSync('node_modules/keystone/templates/layout/base.jade', newContent);
console.log("Applied styling!");

All 29 comments

Would a relative import work?

.
|____node_modules
| |____keystone
| | |____public
| | | |____styles
| | | | |____keystone.less // @import "../../../../public/styles/keystone.less"; at the very bottom
|____public
| |____styles
| | |____keystone.less // This will overwrite the UI

For example, with the above structure, you could import a less file that resides in the user's styles directory and not touch node_modules.

It would, but I think we'd need to test for the file then dynamically add the line to Keystone's keystone.less file or the LESS parser would throw on projects where it wasn't included.

I think the right path for a custom .less file to override the admin UI theme would be ./keystone/admin/theme.less, inline with suggestions in #218

I'd like to explore being able to add custom .js files to the admin UI as well, so maybe a public directory would work better, e.g. ./keystone/admin/public/theme.less...

The other option is for us to add an option that includes a different less file altogether in the admin UI, which could then include either Keystone's theme or base .less file, depending on whether you want a few tweaks or a complete overhaul to the theme.

Not sure how to get around overly-complicated paths in your custom less if we do that, might be a trade-off we have to make for now.

If you guys are held up on how to integrate this feature couldn't you implement the simplest version now where Keystone takes a URL for a custom CSS file in the config file and then users of keystone can precompile that however they want on the side. I already started doing this myself locally but stopped because when deploying to Heroku it pulls in the node modules and ignores anything I add to the admin locally. If we could just send a variable I could put that CSS anywhere. Just a thought to get the most basic version of theming available rather than waiting 6 more months for something more elegant or complex. If this is something you'd find useful I could put together a PR.

This is something i'm really interested in as well- modifying and extending the admin UI. Is there a safe way to extend the jade templates for the admin ui to add functionality?

Any news on this? Don't want to touch the source or inject on front-end.

@nikolowry you're going to have to as of this writing.

@morenoh149 no worries.

However, I'll share what my solution was for future reference. In addition to allowing theming, it also allows for the admin backend url to be something other than "/keystone". (Edit: In case some someone stumbles onto this later, let this be a notice that some incidental side effects could occur. )

https://gist.github.com/nikolowry/5083ecd1d0499ab5f44c/.


Quick overview:

Because I didn't want to edit the source, I overrode keystone's private methods "routes" and "render". The custom methods check for newly introduced env variables and renders accordingly.

Feel free to me me know, if anything I did could have been implemented easier with the official api or if you foresee causing me trouble in the future.

screenshot from 2015-01-13 18 55 52

I think the landing page could be swapped for a SPA. Provide a clean ajax interface for manipulating the lists and an option that lets users switch off all the other routes.

That way all the user has to do is intercept the /keystone route and it's like you have your own theme right?

It's a start.

It would be awesome to be able to hook features into the backoffice as we do for the NavBar. Are there any plans for it? If not, is a PR welcome?

PR is welcome. Current focus is on adding testing to project and dynamically generating the browserify build at run-time.

I really want this feature too. Need something to be flexible.

a modular structure is required.

if i have to use keystone on 5 different projects, its a minimum requirement to have different admin interfaces else i'll keep on getting confused as to which app I am currently logged into.

plz let it be modular.

Here's an update, this is a major current dev focus right now that I'm undertaking with @jossmac.

  1. We're transitioning to use Elemental UI (which replaces bootstrap). At the end of this process, the Admin UI will be an SPA, and jQuery et al. will be removed. See the elemental-integration branch for progress.
  2. I'm refactoring the express app initialisation process to use Express Router objects. This will streamline integrating Keystone into a custom express app.
  3. I've rewritten our Admin UI browserify process to support dynamic source, which will (shortly) allow you to include your own code in the Admin UI to extend or replace parts of it. It also significantly improves load times.
  4. We'll do something similar with the LESS which will allow you to include your own theme file that can change variables (Elemental is highly customisable), override classes or add your own.
  5. The home page has been redesigned (slightly) and will allow custom components to be provided for the new list 'tiles'.

I expect when all this lands, it'll be released as 0.4.0 because it doesn't seem like the scope of change that should be released in a patch. There may also be some breaking changes, and it will need a fair amount of testing in the wild.

super!!

will be waiting to do extensive testing (Y)

Glad to hear this. Will await patiently.

+1

hi, any news here?

Yeah, this is more-or-less done, but we're working on an admin interface rework (#2566) so we don't recommend doing anything yet since everything will change.

Reworked UI looks great! But I still don't know how to override the new admin styles...

While people wait, just create a task that adds your custom stylesheet to Keystone's admin UI and override the theme.

You can automate that, to run it after updates, with something like this:

    // Load UI template fileu
    const buff = fs.readFileSync('node_modules/keystone/admin/server/templates/index.html');
    const content = buff.toString();

    const styleLink = '<link rel="stylesheet" href="/styles/myStylesheet.css">';

    // If already links to our stylesheet we have nothing else to do
    if (content.includes(styleLink)) {
      return;
    }

    // Add link to our stylesheet at the end of <head>
    const newContent = content.replace('</head>', `${styleLink} \n </head>`);
    fs.writeFileSync('node_modules/keystone/admin/server/templates/index.html', newContent);

@lazamar What file would you put this code snippet in?

@vincentracine This would be the content of a task from Gulp or Grunt. If you are not using any of them you can just create a js file in your root with this snippet and make sure to execute it every time you update keystone. For that you can add a postinstall script in your package.json telling it to execute this snippet.

Here is our working code altered from what @lazamar wrote to use custom styling for the admin UI if this helps anyone!

// Load UI template files
const fs = require('fs');
const buff = fs.readFileSync('node_modules/keystone/templates/layout/base.jade');
const content = buff.toString();

const styleLink = 'link(rel="stylesheet", href="/styles/admin.css")';

// If it already links to our stylesheet we have nothing else to do
if (content.includes(styleLink)) {
    console.log("Admin CSS already applied.");
    return;
}

// Add link to our stylesheet at the end of <head>
const newContent = content.replace('block css', `${styleLink}\n\t\tblock css`);
fs.writeFileSync('node_modules/keystone/templates/layout/base.jade', newContent);
console.log("Applied styling!");

Can anyone suggest a code which can override the keystone.min.css in keystonejs?

u should work with less files. css generated from there @hari419

Tq, but i tried that, its not working...
styles are applied from node modules i.e.., keystone.
can you mention the code which can override that styles?

@hari419 Try this,

// Load UI template files
const fs = require('fs');
const path = require('path');
console.log(__dirname);
const buff = fs.readFileSync(path.join(__dirname, '../node_modules/keystone/admin/server/templates/index.html'));
const content = buff.toString();

const styleLink = '<link rel="stylesheet" href="/styles/admin.css">';

// If it already links to our stylesheet we have nothing else to do
if (content.includes(styleLink)) {
    console.log("Admin CSS already applied.");
    return;
}

// Add link to our stylesheet at the end of <head>
const newContent = content.replace('</head>', `\t${styleLink}\n </head>`);
fs.writeFileSync(path.join(__dirname, '../node_modules/keystone/admin/server/templates/index.html'), newContent);
console.log("Applied styling!");

This seems to be the accepted approach now:

Inside of keystone config

...
'adminui custom styles' : '/path/to/admin.less',
...

Inside of admin.less

@import "../../node_modules/keystone/admin/public/styles/keystone.less";

Override the keystone.less or put in completely your own styles here.

Was this page helpful?
0 / 5 - 0 ratings