Extensions & Integrations
Palette Design Editor
An in-context interface for changing CSS
@apostrophecms-pro/palette
is a module that provides an in-context interface for changing the values of developer-set CSS properties. The values are stored in an Apostrophe piece singleton (like @apostrophecms/global
) and applied to the site whenever the stylesheet link is included in a template. Adjusting values via the palette interface renders changes to the site instantly.
Developers define properties that can be changed by configuring the fields
section of the module. Fields are configured similarly
to other Apostrophe schema fields with some extra properties.
The generated stylesheet is stored in the @apostrophecms/global
document and is available everywhere.
Example configuration
modules: {
// ... project level configuration
// Activate the module
'@apostrophecms-pro/palette': {
fields: {
add: {
backgroundColor: {
type: 'color',
label: 'Page Background',
selector: 'body',
property: 'color'
},
imageWidgetMargins: {
label: 'Vertical space between image widgets',
type: 'range',
selector: ['.c-image-widget', '.c-slideshow-widget'],
property: ['margin-bottom', 'margin-top'],
min: 0,
max: 10,
step: 0.1,
unit: 'rem',
mediaQuery: '(max-width: 59.99em)'
},
buttonShadow: {
name: 'buttonShadow',
label: 'Button Shadow',
type: 'color',
selector: '.c-button',
property: 'box-shadow',
valueTemplate: '0 0 7px 2px %VALUE%'
}
},
group: {
colors: {
label: 'Colors',
fields: [ 'backgroundColor' ]
},
space: {
label: 'Spacing',
fields: [ 'imageWidgetMargins' ]
},
buttons: {
label: 'Buttons',
fields: [ 'buttonShadow' ]
}
}
}
}
}
fields
properties of interest
label
Normal field label.
type
A subset of schema field types. Can take string
, range
, color
, and select
. Using other field types is permitted but not guaranteed.
selector
A string or array of strings to be used as CSS selectors. These are printed as-is, so it is valid to pass things like body
, .template p
, #someId [data-foo]
, even :root
, etc. All selectors will be used to target the property
property and give the value of the field.
property
A string or array of strings to be used as CSS properties. These are printed as-is and are used in conjunction with all selector
properties and the value of the field itself.
unit
(optional)
A string that is appended after the value of the field is printed as a CSS rule.
valueTemplate
(optional)
Instead of the property value being derived solely from the field value, a template can be passed where the %VALUE%
get replaced with the field value. Useful for complex CSS values that aren't totally being controlled by palette, like box-shadow
.
mediaQuery
(optional)
A string used to wrap a rule in a CSS media query. The format is as follows @media YOURMEDIAQUERY { YOURSELECTOR { YOURPROPERTY YOURUNIT; } }
- Note, using the media query property will only apply that field's value to that media query. You may need multiple fields to fill out the spectrum of sizes.
Nested grouping
Unlike normal Apostrophe schemas, palette is designed to allow a second level of field grouping. Top level groups are represented similar to tabs, while a second level allows you to group like-elements into collapsable accordions.
// Example of nested grouping
fields: {
add: { ... }, // Your fields
group: {
tabOne: {
label: 'This is Tab One',
fields: [ 'background', 'border', 'buttonColor' ]
},
tabTwo: {
label: 'This is Tab Two',
group: {
accordionOne: {
label: 'Base Typography',
fields: [ 'baseFont', 'baseSize' ]
},
accordionTwo: {
label: 'Heading Typography',
fields: [ 'headingFont', 'HeadingSize' ]
}
},
fields: [ 'ungroupedField', 'stillInTabTwo' ]
}
}
}
Using CSS custom properties (variables)
In addition to targeting element selectors directly you can also target custom property variables. This is useful when allowing editors to control more general site properties that might be applied in several ways. For example, your site's accent color might be a button background for one element and a border in another.
// Example of CSS variable
fields: {
add: {
accentColor: {
type: 'color',
label: 'Site Accent Color',
selector: ':root',
property: '--accent-color'
}
}
}
You can now access accentColor
in your normal CSS.
.button {
background-color: var(--accent-color);
}
.body {
border: 5px solid var(--accent-color);
}
Including the interface and stylesheets in your template
You don't have to do anything. They are automatically injected.
What actually happens?
When the global document is modified, a new palette stylesheet property is generated. The rest of the time no work is needed on each page request, and browser-side caching is also used. A version identifier is included in the style tag to ensure no stale styles are served. To preview edits on the fly, a style
tag is updated dynamically with the latest stylesheet.
Custom rendering
By default, the code in the render.js
file of this module is used on both front and back end
to convert palette properties to CSS with the specified selectors and properties.
This behavior can be customized in two steps:
Enable the
serverRendered: true
option of this module.Override the
getStylesheet(doc)
method of this module to acceptdoc
(the palette piece) and return the CSS markup of your choice.
When serverRendered: true
is in use this method may be an async
function (it may return a promise).
serverRendered: true
must be set in order to ensure all rendering requests flow to your overridden
server-side method, even if they come from the UI code during editing. By default, these are handled
on the browser side, which is very fast but rules out overrides.
Note that the schema of palette fields can be accessed via self.schema
inside your
getStylesheet
function.
Your function is called only when settings have changed, and invocations are automatically debounced to prevent simultaneous invocation and excessive CPU use while editing.