From 83cc97994fa73c19832fa1dc0a2ec790e6f29064 Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Wed, 22 Jan 2025 10:56:09 +1000 Subject: [PATCH] FEATURE: Handle special font properties from discourse-fonts (#30891) In https://github.com/discourse/discourse-fonts/pull/15 we are introducing special font properties for certain fonts, specifically the `font-variation-settings` and `font-feature-settings`. For now this will only apply to Inter, but we may do it for other fonts in future. This commit makes it so the color_definitions.css file includes these special properties for each font, either defined on the root `html` element for the body font or on the `h1-h6` elements for the heading font. This is done in this way because defining them on `@font-face` is ignored by the browser. This also ensures special CSS classes for the wizard container e.g. wizard-container-font-FONTID are defined, this is so we can use these special properties scoped to the font selected in the wizard, which will affect the way the canvas preview is rendered. Here is an example of before/after with special properties applied to Inter, in this case: ```css font-variation-settings: 'opsz' 28; font-feature-settings: 'calt' 0, 'ccmp' 0, 'ss02' 1; ``` --- Gemfile.lock | 2 +- .../fields/styling-preview/-preview-base.js | 12 +++-- .../fields/styling-preview/index.js | 10 ++-- .../static/wizard/components/wizard-step.gjs | 17 ++++++- lib/stylesheet/importer.rb | 51 +++++++++++++++++-- 5 files changed, 78 insertions(+), 14 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e8683fcf525..5cc4fab75d4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -125,7 +125,7 @@ GEM diffy (3.4.3) digest (3.1.1) digest-xxhash (0.2.9) - discourse-fonts (0.0.14) + discourse-fonts (0.0.15) discourse-seed-fu (2.3.12) activerecord (>= 3.1) activesupport (>= 3.1) diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js index 2dd0e595af3..c6cb19e1e5a 100644 --- a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js +++ b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js @@ -100,6 +100,10 @@ export default class PreviewBase extends Component { } loadFontVariants(font) { + if (!font) { + return Promise.resolve(); + } + const fontVariantData = this.fontMap[font.id]; // System font for example does not need to load from a remote source. @@ -206,8 +210,8 @@ export default class PreviewBase extends Component { const options = { ctx, colors, - font: font?.label, - headingFont: headingFont?.label, + font, + headingFont, width: this.width, height: this.height, }; @@ -337,7 +341,7 @@ export default class PreviewBase extends Component { const badgeHeight = headerHeight * 2 * 0.25; const headerMargin = headerHeight * 0.2; const fontSize = Math.round(badgeHeight * 0.5); - ctx.font = `${fontSize}px '${font}'`; + ctx.font = `${fontSize}px '${font.label}'`; const allCategoriesText = i18n( "wizard.homepage_preview.nav_buttons.all_categories" @@ -406,7 +410,7 @@ export default class PreviewBase extends Component { ); ctx.fill(); - ctx.font = `${fontSize}px '${font}'`; + ctx.font = `${fontSize}px '${font.label}'`; ctx.fillStyle = colors.secondary; const pillButtonTextY = headerHeight + headerMargin * 1.4 + fontSize; const firstTopMenuItemX = headerMargin * 3.0 + categoriesBoxWidth; diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/index.js b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/index.js index 2bfa36d2ba3..f44b24806b6 100644 --- a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/index.js +++ b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/index.js @@ -137,12 +137,12 @@ export default class Index extends PreviewBaseComponent { // Topic title ctx.beginPath(); ctx.fillStyle = colors.primary; - ctx.font = `700 ${titleFontSize}em '${headingFont}'`; + ctx.font = `700 ${titleFontSize}em '${headingFont.label}'`; ctx.fillText(i18n("wizard.previews.topic_title"), margin, height * 0.3); // Topic OP text const bodyFontSize = 1; - ctx.font = `${bodyFontSize}em '${font}'`; + ctx.font = `${bodyFontSize}em '${font.label}'`; let verticalLinePos = 0; const topicOp = i18n("wizard.homepage_preview.topic_ops.what_books"); @@ -160,7 +160,7 @@ export default class Index extends PreviewBaseComponent { } ); - ctx.font = `${bodyFontSize}em '${font}'`; + ctx.font = `${bodyFontSize}em '${font.label}'`; // Share button const shareButtonWidth = @@ -221,13 +221,13 @@ export default class Index extends PreviewBaseComponent { // Timeline post count const postCountY = height * 0.3 + margin + 10; ctx.beginPath(); - ctx.font = `700 ${bodyFontSize}em '${font}'`; + ctx.font = `700 ${bodyFontSize}em '${font.label}'`; ctx.fillStyle = colors.primary; ctx.fillText("1 / 20", timelineX + margin / 2, postCountY); // Timeline post date ctx.beginPath(); - ctx.font = `${bodyFontSize * 0.9}em '${font}'`; + ctx.font = `${bodyFontSize * 0.9}em '${font.label}'`; ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 70, 65); ctx.fillText( "Nov 22", diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/wizard-step.gjs b/app/assets/javascripts/discourse/app/static/wizard/components/wizard-step.gjs index 23c1aabe937..bce36e6e192 100644 --- a/app/assets/javascripts/discourse/app/static/wizard/components/wizard-step.gjs +++ b/app/assets/javascripts/discourse/app/static/wizard/components/wizard-step.gjs @@ -6,6 +6,7 @@ import didInsert from "@ember/render-modifiers/modifiers/did-insert"; import didUpdate from "@ember/render-modifiers/modifiers/did-update"; import { schedule } from "@ember/runloop"; import { htmlSafe } from "@ember/template"; +import concatClass from "discourse/helpers/concat-class"; import emoji from "discourse/helpers/emoji"; import { i18n } from "discourse-i18n"; import WizardField from "./wizard-field"; @@ -80,6 +81,20 @@ export default class WizardStepComponent extends Component { return !!this.step.fields.find((f) => f.showInSidebar); } + get containerFontClasses() { + let fontClasses = ""; + + if (this.wizard.font) { + fontClasses += ` wizard-container-body-font-${this.wizard.font.id}`; + } + + if (this.wizard.headingFont) { + fontClasses += ` wizard-container-heading-font-${this.wizard.headingFont.id}`; + } + + return fontClasses; + } + @action stepChanged() { this.saving = false; @@ -195,7 +210,7 @@ export default class WizardStepComponent extends Component { {{/if}} -
+
{{#if @step.emoji}} diff --git a/lib/stylesheet/importer.rb b/lib/stylesheet/importer.rb index 855ac522bc8..5d53844b295 100644 --- a/lib/stylesheet/importer.rb +++ b/lib/stylesheet/importer.rb @@ -42,19 +42,23 @@ module Stylesheet contents = +"" contents << <<~CSS if body_font.present? + // Body font definition #{font_css(body_font)} - + #{render_font_special_properties(body_font, "body")} :root { --font-family: #{body_font[:stack]}; } + CSS contents << <<~CSS if heading_font.present? + // Heading font definition #{font_css(heading_font)} - + #{render_font_special_properties(heading_font, "heading")} :root { --heading-font-family: #{heading_font[:stack]}; } + CSS contents @@ -90,6 +94,7 @@ module Stylesheet font-family: #{font[:stack]}; } CSS + contents << render_wizard_font_special_properties(font) end contents @@ -108,7 +113,10 @@ module Stylesheet if resolved_ids theme = Theme.find_by_id(theme_id) - contents << theme&.scss_variables.to_s + + contents << "\n\n// Theme SCSS variables\n\n" + contents << theme&.scss_variables.to_s.split(";").join(";\n") + ";\n\n" + contents << "\n\n" Theme .list_baked_fields(resolved_ids, :common, :color_definitions) .each do |field| @@ -220,6 +228,7 @@ module Stylesheet "url(\"#{fonts_dir}/#{variant[:filename]}?v=#{DiscourseFonts::VERSION}\") format(\"#{variant[:format]}\")" end ) + contents << <<~CSS @font-face { font-family: '#{font[:name]}'; @@ -232,5 +241,41 @@ module Stylesheet contents end + + def render_font_special_properties(font, font_type) + contents = +"" + root_elements = font_type == "body" ? "html" : "h1, h2, h3, h4, h5, h6" + contents << "#{root_elements} {\n" + contents << font_special_properties(font) + contents << "}\n" + end + + def render_wizard_font_special_properties(font) + contents = +"" + contents << ".wizard-container-heading-font-#{font[:key]} {\n" + contents << " h1, h2, h3, h4, h5, h6 {\n" + contents << font_special_properties(font, 4) + contents << " }\n" + contents << "}\n" + contents << ".wizard-container-body-font-#{font[:key]} {\n" + contents << font_special_properties(font) + contents << "}\n" + end + + def font_special_properties(font, indent = 2) + contents = +"" + if font[:font_variation_settings].present? + contents << "#{" " * indent}font-variation-settings: #{font[:font_variation_settings]};\n" + else + contents << "#{" " * indent}font-variation-settings: normal;\n" + end + + if font[:font_feature_settings].present? + contents << "#{" " * indent}font-feature-settings: #{font[:font_feature_settings]};\n" + else + contents << "#{" " * indent}font-feature-settings: normal;\n" + end + contents + end end end