Skip to content

Commit

Permalink
Merge pull request #606 from jenspav/dark-mode
Browse files Browse the repository at this point in the history
Experimental dark mode
  • Loading branch information
jenspav authored Oct 2, 2024
2 parents f947984 + 9d8939b commit 95cd18a
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ architecture model:
| `generatr.site.externalTag` | Software systems containing this tag will be considered external | | |
| `generatr.site.nestGroups` | Will show software systems in the left side navigator in collapsable groups | `false` | `true` |
| `generatr.site.cdn` | Specifies the CDN base location for fetching NPM packages for browser runtime dependencies. Defaults to jsDelivr, but can be changed to e.g. an on-premise location. | `https://cdn.jsdelivr.net/npm` | `https://cdn.my-company/npm` |
| `generatr.site.theme` | Experimental: allows to force a light or dark theme or allows to switch between light and dark mode on the website with browser preference or menu item. Possible values are 'light', 'dark' or 'auto'. Note that the 'structurizr' exporter (see 'generatr.site.exporter' setting) generally works better for the dark theme. | `light` | `auto` |

See the included example for usage of some those properties in the
[C4 architecture model example](https://github.com/avisi-cloud/structurizr-site-generatr/blob/main/docs/example/workspace.dsl#L163).
Expand Down
3 changes: 2 additions & 1 deletion docs/example/workspace.dsl
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,11 @@ workspace "Big Bank plc" "This is an example workspace to illustrate the key fea
// default behaviour, if no generatr.markdown.flexmark.extensions property is specified, is to load the Tables extension only
"generatr.markdown.flexmark.extensions" "Abbreviation,Admonition,AnchorLink,Attributes,Autolink,Definition,Emoji,Footnotes,GfmTaskList,GitLab,MediaTags,Tables,TableOfContents,Typographic"

"generatr.site.exporter" "c4"
"generatr.site.exporter" "structurizr"
"generatr.site.externalTag" "External System"
"generatr.site.nestGroups" "false"
"generatr.site.cdn" "https://cdn.jsdelivr.net/npm"
"generatr.site.theme" "auto"
}

systemlandscape "SystemLandscape" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fun copySiteWideAssets(exportDir: File) {
copySiteWideAsset(exportDir, "/css/treeview.css")
copySiteWideAsset(exportDir, "/js/treeview.js")
copySiteWideAsset(exportDir, "/js/katex-render.js")
copySiteWideAsset(exportDir, "/js/toggle-theme.js")
}

private fun copySiteWideAsset(exportDir: File, asset: String) {
Expand Down Expand Up @@ -112,10 +113,6 @@ private fun generateStyle(context: GeneratorContext, branchDir: File) {
color: $secondary!important;
background-color: $primary!important;
}
.input.has-site-branding {
color: dimgrey!important;
background-color: white!important;
}
.input.has-site-branding:focus {
border-color: $secondary!important;
box-shadow: 0 0 0 0.125em $secondary;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class HeaderBarViewModel(pageViewModel: PageViewModel, generatorContext: Generat
.map { BranchHomeLinkViewModel(pageViewModel, it) }
val currentBranch = generatorContext.currentBranch
val version = generatorContext.version
val allowToggleTheme = pageViewModel.allowToggleTheme

private fun logoPath(generatorContext: GeneratorContext) =
generatorContext.workspace.views.configuration.properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,16 @@ abstract class PageViewModel(protected val generatorContext: GeneratorContext) {
val includedSoftwareSystems = generatorContext.workspace.includedSoftwareSystems
val configuration = generatorContext.workspace.views.configuration.properties
val includeTreeview = configuration.getOrDefault("generatr.site.nestGroups", "false").toBoolean()
val theme = configuration.getOrDefault("generatr.site.theme", "light").toTheme()
val allowToggleTheme = theme == Theme.AUTO

abstract val url: String
abstract val pageSubTitle: String
}

fun String.toTheme() = when (this) {
"light" -> Theme.LIGHT
"dark" -> Theme.DARK
"auto" -> Theme.AUTO
else -> throw IllegalArgumentException("Unknown theme '$this', allowed values are 'light', 'dark' or 'auto'")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package nl.avisi.structurizr.site.generatr.site.model

enum class Theme {
LIGHT, DARK, AUTO
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ package nl.avisi.structurizr.site.generatr.site.views
import kotlinx.html.*
import nl.avisi.structurizr.site.generatr.site.asUrlToFile
import nl.avisi.structurizr.site.generatr.site.model.PageViewModel
import nl.avisi.structurizr.site.generatr.site.model.Theme

fun HTML.page(viewModel: PageViewModel, block: DIV.() -> Unit) {
attributes["lang"] = "en"
attributes["data-theme"] = "light"
classes = setOf("has-background-light")
when (viewModel.theme) {
Theme.LIGHT -> {
attributes["data-theme"] = "light"
}
Theme.DARK -> {
attributes["data-theme"] = "dark"
}
Theme.AUTO -> { }
}

headFragment(viewModel)
bodyFragment(viewModel, block)
Expand All @@ -24,6 +32,8 @@ private fun HTML.headFragment(viewModel: PageViewModel) {
script(type = ScriptType.textJavaScript, src = "../" + "/modal.js".asUrlToFile(viewModel.url)) { }
script(type = ScriptType.textJavaScript, src = "../" + "/svg-modal.js".asUrlToFile(viewModel.url)) { }
script(type = ScriptType.textJavaScript, src = viewModel.cdn.svgpanzoomJs()) { }
if (viewModel.allowToggleTheme)
script(type = ScriptType.textJavaScript, src = "../" + "/toggle-theme.js".asUrlToFile(viewModel.url)) { }

if (viewModel.includeTreeview)
link(rel = "stylesheet", href = "../" + "/treeview.css".asUrlToFile(viewModel.url))
Expand Down Expand Up @@ -59,7 +69,7 @@ private fun HTML.bodyFragment(viewModel: PageViewModel, block: DIV.() -> Unit) {
div(classes = "site-layout") {
id = "site"
menu(viewModel.menu, viewModel.includeTreeview)
div(classes = "container is-fluid has-background-white") {
div(classes = "container is-fluid") {
block()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fun BODY.pageHeader(viewModel: HeaderBarViewModel) {
div(classes = "navbar-menu has-site-branding") {
div(classes = "navbar-end") {
div(classes = "navbar-item") {
input(classes = "input is-small is-rounded has-site-branding") {
input(classes = "input is-small is-rounded") {
id = "search"
type = InputType.search
size = "30"
Expand All @@ -46,6 +46,15 @@ fun BODY.pageHeader(viewModel: HeaderBarViewModel) {
+branchLink.title
}
}
if (viewModel.allowToggleTheme) {
hr(classes = "navbar-divider")
a(
classes = "navbar-item",
) {
onClick = "toggleTheme()"
+"Toggle theme"
}
}
hr(classes = "navbar-divider")
div(classes = "navbar-item has-text-grey-light") {
span { +"v" }
Expand Down
12 changes: 12 additions & 0 deletions src/main/resources/assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,15 @@ a.navbar-item:hover {
color: #485fc7;
border-bottom-color: #485fc7;
}

.tabs li {
font-weight: bold;
}

.input {
color: dimgrey!important;
background-color: white!important;
}
.input::placeholder {
color: darkgrey!important;
}
18 changes: 18 additions & 0 deletions src/main/resources/assets/js/toggle-theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
if (!localStorage.getItem("data-theme")) {
const prefersDarkMode = window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches;

localStorage.setItem("data-theme", prefersDarkMode ? "dark" : "light");
}

document.documentElement.setAttribute("data-theme", localStorage.getItem("data-theme"));

function toggleTheme() {
if (localStorage.getItem("data-theme") === "light") {
document.documentElement.setAttribute("data-theme", "dark");
localStorage.setItem("data-theme", "dark");
} else {
document.documentElement.setAttribute("data-theme", "light");
localStorage.setItem("data-theme", "light");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import assertk.assertions.containsExactly
import assertk.assertions.isEqualTo
import assertk.assertions.isFalse
import assertk.assertions.isTrue
import org.junit.jupiter.api.DynamicTest
import org.junit.jupiter.api.TestFactory
import kotlin.test.Test

class HeaderBarViewModelTest : ViewModelTest() {
Expand Down Expand Up @@ -67,4 +69,25 @@ class HeaderBarViewModelTest : ViewModelTest() {

assertThat(viewModel.hasLogo).isFalse()
}

@TestFactory
fun `no dark mode`() = listOf(
"light" to false,
"dark" to false,
"auto" to true,
).map { (theme, allowToggle) ->
DynamicTest.dynamicTest(theme) {
generatorContext.workspace.views.configuration.addProperty(
"generatr.site.theme",
theme
)

val viewModel = HeaderBarViewModel(object : PageViewModel(generatorContext) {
override val url: String = "/master/system"
override val pageSubTitle: String = "subtitle"
}, generatorContext)

assertThat(viewModel.allowToggleTheme).isEqualTo(allowToggle)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import org.junit.jupiter.api.TestFactory

class CDNTest {
@TestFactory
fun `cdn locations`() {
fun `cdn locations`() : List<DynamicTest> {
val workspace = Workspace("workspace name", "")
val cdn = CDN(workspace)

listOf(
return listOf(
cdn.bulmaCss() to "/css/bulma.min.css",
cdn.katexJs() to "/dist/katex.min.js",
cdn.katexCss() to "/dist/katex.min.css",
Expand Down

0 comments on commit 95cd18a

Please sign in to comment.