My proposal is to limit course theming to a system of variables linked between components. For the most part, our variables are unique to each component, so two components that ought to be identical can have entirely different styling within a single theme. By generalizing the way we use variables so that they can be applied across components, courses can take on new cohesive styles in a matter of keystrokes.
Taking inspiration from Salesforce Lightning's concept of Design Tokens, I've proposed a handful of primitives, the fundamental elements of a UI theme that can be mapped to variables and shared between similar components. While similar to atoms in Brad Frost's Atomic Design methodology, our team struggled with defining the taxonomy (If text requires color and color is an atom, then shouldn't typography be a molecule?). The purpose of primitives is to be abstract — they don't care about size or granularity; they're simply the CSS options that you want to expose to course builders and designers.
Typography & Icons
A robust type scale will use a more dramatic ratio for larger screens and a more gradual one for mobile devices. This can be done manually with media queries or systematically, using calculations and ratios.
The most pronounced differences between text sizes should be in headers, with little (if any) variation in body text between devices.
A single color ought to be used to indicate primary actions to a user. Using this as a starting point, an expanded palette can be generated to accomodate for the range of UI states and accessibility considerations.
In addition to the primary color, 3 dark and 3 light variants covers most UI needs.
Semantic colors are those which communicate status to users through either convention or their repeated use outside of the primary color. Generally, these are your feedback or status colors, and are used in related components.
A range of neutral colors can be used for text, containers, background colors, and disabled states. To ensure adequate color contrast ratios accessibility, a theme should provide colors for: dark text on light, light text on dark, text on primary, text on semantic.
For consistency and scalability, margin and padding within and between elements on a page should use a constrained set of values. Using a similar approach as a typography scale, you can generate spacers, or utilities/variables to adjust margin & padding on one side, two sides on the same axis, or all sides of an element.
Certain components ought to share similar sizing, so it makes sense to link associated variables together. Buttons and input fields would likely share similar padding, as would a modal and a card, etc.
This could also factor into better responsive design, if set up correctly - if shared spacing variables were to change at breakpoints, it would reduce the need for component-specific media queries. We would likely need separate spacing scales for fixed and responsive attributes. It makes sense for a modal to have tighter padding on mobile, while a button might need to grow to provide a larger tap target.
A set of increasing shadow values can correspond to an implied z-axis to communicate hierarchy in a system.
Elements that obscure others or that require user attention should have a higher value. These patterns are generally well-established, and can be baked into components. Additionally, shadows can provide visual feedback to users when values are changed upon interacting with an element (ie. hovering or activating a button or hotspot).
Shadows are unique among primitives in that they are entirely optional; if you want a fully flat UI, simply set all shadow values to 0.
Along with typography, borders are among the variables that most affect the personality of a theme. Certain families of components should share similar styling in border radius, width, color, and style. Borders are difficult to define systematically since nearly every component uses them differently, but we can begin by identifying common values, such as those shared between cards, inputs fields, buttons.