diff --git a/_extensions/closeread/_extension.yml b/_extensions/closeread/_extension.yml index 0576f88..fe16904 100644 --- a/_extensions/closeread/_extension.yml +++ b/_extensions/closeread/_extension.yml @@ -7,7 +7,7 @@ contributes: html: filters: - closeread.lua - theme: [grid.scss] + css: closeread.css page-layout: full execute: echo: false diff --git a/_extensions/closeread/closeread.css b/_extensions/closeread/closeread.css new file mode 100644 index 0000000..35358cc --- /dev/null +++ b/_extensions/closeread/closeread.css @@ -0,0 +1,149 @@ +/* global layout styles */ +.cr-section { + display: grid; + grid-template-rows: 1fr; +} +.cr-section .narrative-col { + grid-row: 1; +} +.cr-section .narrative-col .narrative { + padding-block: 45svh; + padding-inline: 1em; +} +.cr-section .narrative-col .narrative p { + margin-bottom: 0; +} +.cr-section .sticky-col { + grid-row: 1; +} +.cr-section .sticky-col .sticky-col-stack { + display: grid; + height: 100dvh; + position: sticky; + overflow: hidden; + top: 0; +} +.cr-section .sticky-col .sticky-col-stack .sticky { + grid-area: 1/1; + margin: auto; + opacity: 0; + transition: opacity linear 0.5s, color 1s linear, transform 1s ease-in-out, transform-origin 1s ease-in-out; +} +.cr-section .sticky-col .sticky-col-stack .cr-active { + opacity: 1; +} + +/* mobile sizing (bootstrap: xs) is always overlay-center */ +@media (max-width: 575.98px) { + body.overlay-center .cr-layout { + grid-template-columns: 1fr; + } + body.overlay-center .cr-layout .narrative-col { + grid-column: 1; + z-index: 1; + margin-inline: 0.5em; + } + body.overlay-center .cr-layout .sticky-col { + grid-column: 1; + } +} +.overlay-left, +.overlay-center, +.overlay-right { + grid-template-columns: 1fr; +} +.overlay-left .narrative-col, +.overlay-center .narrative-col, +.overlay-right .narrative-col { + grid-column: 1; + z-index: 1; +} +.overlay-left .sticky-col, +.overlay-center .sticky-col, +.overlay-right .sticky-col { + grid-column: 1; +} + +.overlay-left .narrative-col { + margin-inline-start: 5%; + margin-inline-end: 65%; +} + +.overlay-center .narrative-col { + margin-inline: auto; +} + +.overlay-right .narrative-col { + margin-inline-start: 65%; + margin-inline-end: 5%; +} + +.sidebar-left { + grid-template-columns: 1fr 2fr; +} +.sidebar-left .narrative-col { + grid-column: 1; + margin-inline: 0; +} +.sidebar-left .sticky-col { + grid-column: 2; +} + +.sidebar-right { + grid-template-columns: 2fr 1fr; +} +.sidebar-right .narrative-col { + grid-column: 2; + margin-inline: 0; +} +.sidebar-right .sticky-col { + grid-column: 1; +} + +/* poem styles */ +.cr-poem { + font-family: Iowan Old Style, Apple Garamond, Baskerville, Times New Roman, Droid Serif, Times, Source Serif Pro, serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + /* need large font to stop safari text blurriness, but max-height to stop + other stickies from being pushed down by pre-scaled poem size */ + max-height: 100dvh; + font-size: 300%; + white-space: pre; + line-height: 1em; + transform-origin: center center; +} +.cr-poem.cr-hl-within { + color: rgba(0, 0, 0, 0.5); +} +.cr-poem .cr-hl { + color: rgb(0, 0, 0); + font-weight: bold; +} + +/* debug styles */ +body.cr-debug .cr-poem { + background-color: rgba(255, 255, 0, 0.6); + border: 1px solid orange; +} +body.cr-debug [id^=cr-] { + opacity: 0.8; +} +body.cr-debug .sidebar-col > * { + background: rgba(255, 255, 0, 0.5); + border: 1px solid rgba(255, 166, 0, 0.5); +} +body.cr-debug .sidebar-col > * > * { + background: rgba(0, 208, 255, 0.5); + border: 1px solid rgba(0, 115, 255, 0.5); + border-radius: 5px; +} + +/* remove header option */ +body.cr-removeheaderspace #quarto-content main#quarto-document-content { + padding-top: 0; + margin-top: 0; +} +body.cr-removeheaderspace #quarto-content main#quarto-document-content .quarto-title-block { + display: none; +} + +/*# sourceMappingURL=closeread.css.map */ diff --git a/_extensions/closeread/closeread.css.map b/_extensions/closeread/closeread.css.map new file mode 100644 index 0000000..4f77945 --- /dev/null +++ b/_extensions/closeread/closeread.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["closeread.scss"],"names":[],"mappings":"AAAA;AAEA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;;AAKN;EACE;;AAIA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EAGA;EAEA,YACE;;AAOJ;EACE;;;AAMR;AACA;EAEI;IACE;;EAEA;IACE;IACA;IACA;;EAGF;IACE;;;AASR;AAAA;AAAA;EAIE;;AAEA;AAAA;AAAA;EACE;EACA;;AAGF;AAAA;AAAA;EACE;;;AAMF;EACE;EACA;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAMJ;EACE;;AAEA;EACE;EACA;;AAGF;EACE;;;AAGJ;EACE;;AAEA;EACE;EACA;;AAGF;EACE;;;AAIJ;AAEA;EAEE;AAEA;AAAA;EAEA;EACA;EAEA;EACA;EAGA;;AAGA;EACE;;AAGF;EACE;EACA;;;AAKJ;AAKE;EACE;EACA;;AAIF;EACE;;AAIF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMN;AAII;EACE;EACA;;AAEA;EACE","file":"closeread.css"} \ No newline at end of file diff --git a/_extensions/closeread/scroller-init.js b/_extensions/closeread/closeread.js similarity index 90% rename from _extensions/closeread/scroller-init.js rename to _extensions/closeread/closeread.js index 6838545..7008adc 100644 --- a/_extensions/closeread/scroller-init.js +++ b/_extensions/closeread/closeread.js @@ -1,3 +1,6 @@ +//==============// +// closeread.js // +//==============// /* each "scroller" requires an initiation that tells it: (a) which elements to watch (eg. .step) @@ -5,18 +8,21 @@ although users may have several scrollers in one quarto doc, i think with the right syntax we can get away with a single init block for everyone */ - +// set params const stepSelector = "[data-focus-on]" +// code that will be run when the HTML file is initially loaded by a browser document.addEventListener("DOMContentLoaded", () => { - // if debugging, add .cr-debug to the body - const debugOption = document - .querySelector("meta[cr-debug-mode]")?.getAttribute("cr-debug-mode") - const debugMode = debugOption === "true" + // attach meta classes to + document.body.classList.add("closeread") + const debugMode = getBooleanConfig("debug-mode") + const removeHeaderSpace = getBooleanConfig("remove-header-space") if (debugMode) { - console.info("Close Read: debug mode ON") document.body.classList.add("cr-debug") + } + if (removeHeaderSpace) { + document.body.classList.add("cr-removeheaderspace") } // define ojs variables if the connector module is available @@ -67,7 +73,11 @@ document.addEventListener("DOMContentLoaded", () => { }); - /* updateStickies: fill in with description */ +//=================// +// Update Stickies // +//=================// + + /* updateStickies: triggers effects and transformations of the focused sticky */ function updateStickies(allStickies, focusedStickyName, response) { const focusedSticky = document.querySelectorAll("[id=" + focusedStickyName)[0]; @@ -232,9 +242,9 @@ function scalePoemToSpan(el, highlightIds, paddingX = 75, paddingY = 50) { } -//=================// +//==================// // Transform Sticky // -//=================// +//==================// function transformSticky(sticky, step) { @@ -266,3 +276,12 @@ function transformSticky(sticky, step) { sticky.style.transform = transformStr; } + +/* getBooleanConfig: checks for a with named attribute `cr-[metaFlag]` + and returns true if its value is "true" or false otherwise */ +function getBooleanConfig(metaFlag) { + const option = document + .querySelector("meta[cr-" + metaFlag + "]")?.getAttribute("cr-" + metaFlag) + return option === "true" +} + diff --git a/_extensions/closeread/closeread.lua b/_extensions/closeread/closeread.lua index 0ac0b85..c67acc3 100644 --- a/_extensions/closeread/closeread.lua +++ b/_extensions/closeread/closeread.lua @@ -1,106 +1,63 @@ +--===============-- +-- Closeread.lua -- +--===============-- +-- This script creates the functions/filters that are used to process +-- a closeread document into the initial HTML file that is loaded by +-- the browser. The filters are actually run at the very bottom of the +-- script, so to understand the script it might be easiest to start there. -- set defaults local debug_mode = false -local step_selectors = {["focus-on"] = true} +local trigger_selectors = {["focus-on"] = true} local cr_attributes = {["pan-to"] = true, ["scale-by"] = true} +local remove_header_space = false +local global_layout = "sidebar-left" --- Append attributes to any cr line blocks -function add_attributes(lineblock) - local first_line = lineblock.content[1] - - if first_line[1].t == "Str" and first_line[1].text:sub(1,1) == "{" then - local id = extractIds(first_line)[1] - local classes = extractClasses(first_line) - local attr_tab = extractAttr(first_line) - - table.remove(lineblock.content, 1) - lineblock = pandoc.Div(lineblock, pandoc.Attr(id, classes, attr_tab)) - end - - return lineblock -end -function extractAttr(el) - local attr_tab = {} - local keys_tab = {} - local vals_tab = {} - local key_inds = {} - local ind = 0 - - -- gather keys and their index - for _,v in ipairs(el) do - ind = ind + 1 - if v.t == "Str" then - v.text = v.text:gsub("[{}]", "") - if v.text:sub(-1) == "=" then - table.insert(keys_tab, v.text:sub(1, -2)) - table.insert(key_inds, ind) - end - end - end - - -- gather values from index + 1 - for _,v in ipairs(key_inds) do - if el[v + 1].t == "Quoted" then - table.insert(vals_tab, el[v + 1].content[1].text) - else - table.insert(vals_tab, "") - end +--======================-- +-- Process YAML options -- +--======================-- + +function read_meta(m) + + -- debug mode + if m["debug-mode"] ~= nil then + debug_mode = m["debug-mode"] end - - -- zip them together - for i = 1, #keys_tab do - attr_tab[keys_tab[i]] = vals_tab[i] + + -- remove-header-space + if m["remove-header-space"] ~= nil then + remove_header_space = m["remove-header-space"] end - - return attr_tab -end -function extractIds(el) - local ids = {} - for _,v in ipairs(el) do - if v.t == "Str" then - v.text = v.text:gsub("[{}]", "") - if v.text:sub(1, 1) == "#" then - table.insert(ids, v.text:sub(2)) - end + -- layout options + if m["cr-section"] ~= nil then + if m["cr-section"]["layout"] ~= nil then + global_layout = m["cr-section"]["layout"][1].text end end - return ids -end + -- inject debug mode option in html + quarto.doc.include_text("in-header", "") -function extractClasses(el) - local classes = {} - for _,v in ipairs(el) do - if v.t == "Str" then - if v.text:sub(1, 1) == "." then - table.insert(classes, v.text:sub(2)) - end - end - end - return classes + -- inject remove_header_space options into html + quarto.doc.include_text("in-header", "") + end +--=====================-- +-- Form CR-Section AST -- +--=====================-- --- Read in YAML options -function read_meta(m) - - if m["debug-mode"] ~= nil then - debug_mode = m["debug-mode"] - end - - -- make accessible to scroller-init.js via tag - quarto.doc.include_text("in-header", "") +-- Construct cr section AST +function make_section_layout(div) -end - --- Construct sticky sidebar AST -function make_sidebar_layout(div) - - if div.classes:includes("cr-layout") then + if div.classes:includes("cr-section") then + -- make contents of stick-col sticky_blocks = div.content:walk { traverse = 'topdown', Block = function(block) @@ -114,38 +71,44 @@ function make_sidebar_layout(div) end } - narrative_blocks = div.content:walk { - traverse = 'topdown', - Block = function(block) - -- return only the non-sticky blocks... - if not is_sticky(block) then - -- but check for step blocks - if block.attributes ~= nil and is_step(block) then - -- and and wrap it in an enclosing div - return wrap_block(block) - else - return block - end + -- make contents of narrative-col + narrative_blocks = {} + for _,block in ipairs(div.content) do + if not is_sticky(block) then + if is_trigger(block) then + table.insert(block.attr.classes, "narrative") + table.insert(block.attr.classes, "trigger") + table.insert(narrative_blocks, block) else - return {} + local wrapped_block = pandoc.Div(block, pandoc.Attr("", {"narrative"}, {})) + table.insert(narrative_blocks, wrapped_block) end end - } + end + + -- identify section layout + local section_layout = global_layout -- inherit from doc yaml + for attr, value in pairs(div.attr.attributes) do + if attr == "layout" then + section_layout = value -- but override with section attr + end + end - narrative_col = pandoc.Div(narrative_blocks, - pandoc.Attr("", {"sidebar-col"}, {})) + -- piece together the cr-section + narrative_col = pandoc.Div(pandoc.Blocks(narrative_blocks), + pandoc.Attr("", {"narrative-col"}, {})) sticky_col_stack = pandoc.Div(sticky_blocks, pandoc.Attr("", {"sticky-col-stack"})) sticky_col = pandoc.Div(sticky_col_stack, pandoc.Attr("", {"sticky-col"}, {})) - layout = pandoc.Div({narrative_col, sticky_col}, - pandoc.Attr("", {"cr-layout", "column-screen", table.unpack(div.classes)}, - {style = "grid-template-columns: 1fr 3fr;"})) + cr_section = pandoc.Div({narrative_col, sticky_col}, + pandoc.Attr("", {"column-screen",table.unpack(div.classes), section_layout}, {})) - return layout + return cr_section end end + function shift_id_to_block(block) -- if block contains inlines... @@ -168,20 +131,21 @@ function shift_id_to_block(block) return block end --- wrap_block: wrap step blocks in a div that allows us to style steps visually + +-- wrap_block: wrap trigger blocks in a div that allows us to style triggers visually function wrap_block(block) -- extract attributes local attributesToMove = {} for attr, value in pairs(block.attributes) do - if step_selectors[attr] or cr_attributes[attr] then + if trigger_selectors[attr] or cr_attributes[attr] then attributesToMove[attr] = value block.attributes[attr] = nil end end -- finally construct a pandoc.div with the new details and content to return - return pandoc.Div(block, pandoc.Attr("", {"step"}, attributesToMove)) + return pandoc.Div(block, pandoc.Attr("", {"trigger"}, attributesToMove)) end @@ -209,19 +173,26 @@ function is_sticky(block) return sticky_block_id or sticky_inline_id end --- utility functions -function is_step(block) - local is_step = false - for selector, _ in pairs(step_selectors) do +function is_trigger(block) + -- it can't be a trigger without attributes + if block.attributes == nil then + return false + end + + -- if it has attributes, they must match a selector + local is_trigger = false + for selector, _ in pairs(trigger_selectors) do if block.attributes[selector] then - is_step = true + is_trigger = true break end end - return is_step + + return is_trigger end + function find_in_arr(arr, value) for i, v in pairs(arr) do if v == value then @@ -230,7 +201,97 @@ function find_in_arr(arr, value) end end --- add scrollama.js, the intersection-observer polyfill and our scroller init + +--======================-- +-- Lineblock Attributes -- +--======================-- + +-- Append attributes to any cr line blocks +function add_attributes(lineblock) + local first_line = lineblock.content[1] + + if first_line[1].t == "Str" and first_line[1].text:sub(1,1) == "{" then + local id = extractIds(first_line)[1] + local classes = extractClasses(first_line) + local attr_tab = extractAttr(first_line) + + table.remove(lineblock.content, 1) + lineblock = pandoc.Div(lineblock, pandoc.Attr(id, classes, attr_tab)) + end + + return lineblock +end + + +function extractAttr(el) + local attr_tab = {} + local keys_tab = {} + local vals_tab = {} + local key_inds = {} + local ind = 0 + + -- gather keys and their index + for _,v in ipairs(el) do + ind = ind + 1 + if v.t == "Str" then + v.text = v.text:gsub("[{}]", "") + if v.text:sub(-1) == "=" then + table.insert(keys_tab, v.text:sub(1, -2)) + table.insert(key_inds, ind) + end + end + end + + -- gather values from index + 1 + for _,v in ipairs(key_inds) do + if el[v + 1].t == "Quoted" then + table.insert(vals_tab, el[v + 1].content[1].text) + else + table.insert(vals_tab, "") + end + end + + -- zip them together + for i = 1, #keys_tab do + attr_tab[keys_tab[i]] = vals_tab[i] + end + + return attr_tab +end + + +function extractIds(el) + local ids = {} + for _,v in ipairs(el) do + if v.t == "Str" then + v.text = v.text:gsub("[{}]", "") + if v.text:sub(1, 1) == "#" then + table.insert(ids, v.text:sub(2)) + end + end + end + + return ids +end + + +function extractClasses(el) + local classes = {} + for _,v in ipairs(el) do + if v.t == "Str" then + if v.text:sub(1, 1) == "." then + table.insert(classes, v.text:sub(2)) + end + end + end + return classes +end + + +--================-- +-- HTML Injection -- +--================-- + quarto.doc.add_html_dependency({ name = "intersection-observer-polyfill", version = "1.0.0", @@ -242,15 +303,25 @@ quarto.doc.add_html_dependency({ scripts = {"scrollama.min.js"} }) quarto.doc.add_html_dependency({ - name = "cr-sidebar-scroller-init", + name = "closereadjs", version = "0.0.1", - scripts = {"scroller-init.js"} + scripts = {"closeread.js"} }) --- TODO - add a js scrollama setup step (can i do this with a js script + yaml?) + +--=============-- +-- Run Filters -- +--=============-- return { - {LineBlock = add_attributes}, - {Meta = read_meta, - Div = make_sidebar_layout} + { + Meta = read_meta + }, + { + LineBlock = add_attributes + }, + { + Div = make_section_layout, + Pandoc = add_classes_to_body + } } diff --git a/_extensions/closeread/grid.scss b/_extensions/closeread/closeread.scss similarity index 53% rename from _extensions/closeread/grid.scss rename to _extensions/closeread/closeread.scss index eb78918..416dd91 100644 --- a/_extensions/closeread/grid.scss +++ b/_extensions/closeread/closeread.scss @@ -1,41 +1,30 @@ -/*-- scss:defaults --*/ +/* global layout styles */ -/* TODO: Customize html appearance by setting SCSS variables */ -/* See https://quarto.org/docs/output-formats/html-themes.html#theme-options */ +.cr-section { + display: grid; + grid-template-rows: 1fr; -/*-- scss:rules --*/ + .narrative-col { + grid-row: 1; -/* TODO: Provide custom CSS rules */ + .narrative { + padding-block: 45svh; + padding-inline: 1em; -.cr-layout { - display: grid; - grid-template-columns: 1fr 2fr; - - .sidebar-col { - grid-column: 1; - - /* extended wrapper around content to allow early scroll detection */ - > * { - padding-block-start: 45svh; - padding-block-end: 45svh; - vertical-align: middle; - padding-left: 30px; - padding-right: 30px; - - p { - margin-bottom: 0; - } + p { + margin-bottom: 0; + } } } - + .sticky-col { - grid-column: 2; - + grid-row: 1; + // using a grid to stack sticky elements on top of each other to then // transition through (based on reveal's .r-stack) .sticky-col-stack { display: grid; - height: 100svh; + height: 100dvh; position: sticky; overflow: hidden; top: 0; @@ -46,6 +35,7 @@ // to be overruled when it is the active element opacity: 0; + transition: opacity linear 0.5s, color 1s linear, @@ -61,13 +51,99 @@ } } +/* mobile sizing (bootstrap: xs) is always overlay-center */ +@media (max-width: 575.98px) { + body.overlay-center { + .cr-layout { + grid-template-columns: 1fr; + + .narrative-col { + grid-column: 1; + z-index: 1; + margin-inline: 0.5em; + } + + .sticky-col { + grid-column: 1; + } + } + } +} + +// layouts for wider-than-mobile sizing + +// overlay layouts use one column... +.overlay-left, +.overlay-center, +.overlay-right +{ + grid-template-columns: 1fr; + + .narrative-col { + grid-column: 1; + z-index: 1; + } + + .sticky-col { + grid-column: 1; + } +} + +// ... with inline margins set for narrative content depending on side +.overlay-left { + .narrative-col { + margin-inline-start: 5%; + margin-inline-end: 65%; + } +} +.overlay-center { + .narrative-col { + margin-inline: auto; + } +} +.overlay-right { + .narrative-col { + margin-inline-start: 65%; + margin-inline-end: 5%; + } +} + + +// sidebar layouts use two columns +.sidebar-left { + grid-template-columns: 1fr 2fr; + + .narrative-col { + grid-column: 1; + margin-inline: 0; + } + + .sticky-col { + grid-column: 2; + } +} +.sidebar-right { + grid-template-columns: 2fr 1fr; + + .narrative-col { + grid-column: 2; + margin-inline: 0; + } + + .sticky-col { + grid-column: 1; + } +} + +/* poem styles */ + .cr-poem { font-family: Iowan Old Style, Apple Garamond, Baskerville, Times New Roman, Droid Serif, Times, Source Serif Pro, serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; /* need large font to stop safari text blurriness, but max-height to stop other stickies from being pushed down by pre-scaled poem size */ - max-height: 100vh; + max-height: 100dvh; font-size: 300%; white-space: pre; @@ -115,4 +191,20 @@ body.cr-debug { } } +} + +/* remove header option */ + +body.cr-removeheaderspace { + #quarto-content { + main#quarto-document-content { + padding-top: 0; + margin-top: 0; + + .quarto-title-block { + display: none; + } + } + } + } \ No newline at end of file diff --git a/_extensions/closeread/custom.scss b/_extensions/closeread/custom.scss deleted file mode 100644 index 1165a43..0000000 --- a/_extensions/closeread/custom.scss +++ /dev/null @@ -1,100 +0,0 @@ -/*-- scss:defaults --*/ - -/* TODO: Customize html appearance by setting SCSS variables */ -/* See https://quarto.org/docs/output-formats/html-themes.html#theme-options */ - -/*-- scss:rules --*/ - -/* TODO: Provide custom CSS rules */ - -.columns.cr-sidebar { - - display: flex; - flex-direction: row; - column-gap: 10%; - - // put the sticky content first if we added cr-reverse - &.cr-reverse { - flex-direction: row-reverse; - } - - // vertical space around sidebar elements - // (markdown headings create sections of content that we need to account for) - .column.sidebar_col { - & > *:not(section), - section > * { - padding-block-start: 20svh; - padding-block-end: 20svh; - vertical-align: middle; - } - } - - // sticky positioning - .column.sticky_col { - padding: 20px; - align-self: flex-start; - position: sticky; - top: 30svh; - - // using a grid to stack sticky elements on top of each other to then - // transition through (based on reveal's .r-stack) - .sticky_col_stack { - display: grid; - - [data-cr-id] { - grid-area: 1 / 1; - margin: auto; - // opacity: 0.5; // lowering opacity for debug purposes - - // to be overruled when it is the active element - opacity: 0; - transition: opacity linear 0.5s; - - - } - - // show active elements (this class is applied by scrollama - .cr-active { - opacity: 1; - } - - } - } -} - -/* mobile (overlay) view */ - -@include media-breakpoint-down(lg) { - .columns.cr-sidebar { - flex-direction: column-reverse; - - .column.sidebar_col { - width: 100% !important; - padding-inline: 7.5%; - align-self: center; - z-index: 1; - - - /* wip - this is supposed to target our narrative blocks to make them readable over the sticky content, but since non-step narrative blocks (ie. bare content) also gets spaced, this breaks the spacing. we may need to wrap other blocks too, not just the steps. */ - & > *:not(section, .cr-crossfade), - section > *:not(.cr-crossfade), - .cr-crossfade > * { - padding: 10px; - background-color: #fafafabb; - max-width: 90%; - margin-inline: auto; - } - } - - .column.sticky_col { - width: 100% !important; - align-self: center; - } - } - } - -// debug -.cr-debug .cr-crossfade { - background-color: #ffff0099; - border: 1px solid orange; -} diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 87d4820..84532c4 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -22,8 +22,4 @@ website: format: html: - theme: - - cosmo - - - + theme: [cosmo] diff --git a/docs/gallery/demos/a-poem-about-suffering/index.qmd b/docs/gallery/demos/a-poem-about-suffering/index.qmd index f211674..6036c85 100644 --- a/docs/gallery/demos/a-poem-about-suffering/index.qmd +++ b/docs/gallery/demos/a-poem-about-suffering/index.qmd @@ -1,12 +1,14 @@ --- format: closeread-html: - theme: nytimes.scss + css: nytimes.css code-tools: source: true + # note that you can't see code-tools when this option is true + remove-header-space: true --- -:::{.cr-layout} +:::{.cr-section} :::{} # A Poem (and a Painting) About the Suffering That Hides in Plain Sight diff --git a/docs/gallery/demos/a-poem-about-suffering/nytimes.css b/docs/gallery/demos/a-poem-about-suffering/nytimes.css new file mode 100644 index 0000000..f03d702 --- /dev/null +++ b/docs/gallery/demos/a-poem-about-suffering/nytimes.css @@ -0,0 +1,93 @@ +@import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap"); +#quarto-document-content { + font-family: Georgia, "Times New Roman", Times, serif; + font-size: 15pt; +} +#quarto-document-content h1 { + font-family: "Cormorant Garamond", "Times New Roman", Times, serif; + font-weight: 500; + font-size: 2.8em; + line-height: 1; +} + +.cr-layout { + display: grid; + grid-template-columns: 400px 1fr; +} +.cr-layout .sidebar-col { + grid-column: 1; + background-color: rgb(17, 17, 17); + color: white; + min-width: 400px; + /* extended wrapper around content to allow early scroll detection */ + /* Override the padding for the first block */ +} +.cr-layout .sidebar-col > * { + padding-block-start: 45svh; + padding-block-end: 45svh; + vertical-align: middle; + padding-left: 30px; + padding-right: 30px; +} +.cr-layout .sidebar-col > * p { + margin-bottom: 0; +} +.cr-layout .sidebar-col > *:first-child { + padding-block-start: 10svh; +} +.cr-layout .sticky-col { + grid-column: 2; +} +.cr-layout .sticky-col .sticky-col-stack { + display: grid; + height: 100svh; + position: sticky; + top: 0; +} +.cr-layout .sticky-col .sticky-col-stack [id^=cr-] { + grid-area: 1/1; + margin: auto; + opacity: 0; + transition: opacity linear 0.5s, color 1s linear, transform 1s ease-in-out, transform-origin 1s ease-in-out; +} +.cr-layout .sticky-col .sticky-col-stack .cr-active { + opacity: 1; +} + +.cr-poem { + font-family: Iowan Old Style, Apple Garamond, Baskerville, Times New Roman, Droid Serif, Times, Source Serif Pro, serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + /* need large font to stop safari text blurriness, but max-height to stop + other stickies from being pushed down by pre-scaled poem size */ + max-height: 100vh; + font-size: 300%; + white-space: pre; + line-height: 1em; + transform-origin: center center; +} +.cr-poem.cr-hl-within { + color: rgba(0, 0, 0, 0.5); +} +.cr-poem .cr-hl { + color: rgb(0, 0, 0); + font-weight: bold; +} + +/* debug styles */ +body.cr-debug .cr-poem { + background-color: rgba(255, 255, 0, 0.6); + border: 1px solid orange; +} +body.cr-debug [id^=cr-] { + opacity: 0.8; +} +body.cr-debug .sidebar-col > * { + background: rgba(255, 255, 0, 0.5); + border: 1px solid rgba(255, 166, 0, 0.5); +} +body.cr-debug .sidebar-col > * > * { + background: rgba(0, 208, 255, 0.5); + border: 1px solid rgba(0, 115, 255, 0.5); + border-radius: 5px; +} + +/*# sourceMappingURL=nytimes.css.map */ diff --git a/docs/gallery/demos/a-poem-about-suffering/nytimes.css.map b/docs/gallery/demos/a-poem-about-suffering/nytimes.css.map new file mode 100644 index 0000000..4642c01 --- /dev/null +++ b/docs/gallery/demos/a-poem-about-suffering/nytimes.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["nytimes.scss"],"names":[],"mappings":"AACQ;AAGR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;AAEA;AAaA;;AAZA;EACE;EACA;EACA;EACA;EACA;;AAEF;EACE;;AAKF;EACE;;AAKJ;EACE;;AAIA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EAGA;EACA,YACE;;AAOJ;EACE;;;AAMR;EAEE;AAEA;AAAA;EAEA;EACA;EAEA;EACA;EAGA;;AAGA;EACE;;AAGF;EACE;EACA;;;AAKJ;AAKE;EACE;EACA;;AAIF;EACE;;AAIF;EACE;EACA;;AAEA;EACE;EACA;EACA","file":"nytimes.css"} \ No newline at end of file diff --git a/docs/gallery/demos/a-poem-about-suffering/nytimes.scss b/docs/gallery/demos/a-poem-about-suffering/nytimes.scss index 42702d0..d23302c 100644 --- a/docs/gallery/demos/a-poem-about-suffering/nytimes.scss +++ b/docs/gallery/demos/a-poem-about-suffering/nytimes.scss @@ -1,14 +1,6 @@ @import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap"); -/*-- scss:defaults --*/ - -/* TODO: Customize html appearance by setting SCSS variables */ -/* See https://quarto.org/docs/output-formats/html-themes.html#theme-options */ - -/*-- scss:rules --*/ - -/* TODO: Provide custom CSS rules */ #quarto-document-content { font-family: Georgia, "Times New Roman", Times, serif; diff --git a/docs/gallery/demos/minard-zoom/minard.qmd b/docs/gallery/demos/minard-zoom/minard.qmd index 355f277..5bbe95d 100644 --- a/docs/gallery/demos/minard-zoom/minard.qmd +++ b/docs/gallery/demos/minard-zoom/minard.qmd @@ -1,11 +1,13 @@ --- title: "Minard's Map" format: - closeread-html + closeread-html: + cr-section: + layout: sidebar-right --- -:::{.cr-layout} +:::{.cr-section} There once was a French engineer names Charles Minard who put his mind to creating a map that illustrated Napoleon's retreat from Moscow. diff --git a/docs/gallery/demos/ojs-map/index.qmd b/docs/gallery/demos/ojs-map/index.qmd index 15b3dbe..18b1b65 100644 --- a/docs/gallery/demos/ojs-map/index.qmd +++ b/docs/gallery/demos/ojs-map/index.qmd @@ -49,7 +49,7 @@ Now let's load in some land, so we can distinguish it from ocean: world = FileAttachment("naturalearth-land-110m.geojson").json() ``` -::::{.cr-layout} +::::{.cr-section layout="overlay-center"} :::{focus-on="map"} We want our globe to rotate with the scroll progress — between -180 and 180. diff --git a/docs/gallery/demos/sticky-block-types/index.qmd b/docs/gallery/demos/sticky-block-types/index.qmd index ae82bf3..b6766b5 100644 --- a/docs/gallery/demos/sticky-block-types/index.qmd +++ b/docs/gallery/demos/sticky-block-types/index.qmd @@ -1,10 +1,11 @@ --- format: closeread-html: - debug-mode: false + cr-section: + layout: overlay-right --- -::::{.cr-layout} +::::{.cr-section} # Test other sticky block types @@ -36,7 +37,7 @@ I've replaced its margin with some padding to demonstrate that you can give it a Here's a little non-scrolling interlude before our next scrolly section! :::: -::::{.cr-layout} +::::{.cr-section layout="sidebar-left"} :::{focus-on="scatter"} This is a scatterplot. @@ -165,11 +166,3 @@ graph G { ::: # The End - - -:::{.counter style="position: fixed; top: 10px; right: 10px; background-color: skyblue; border-radius: 5px; padding: 18px 18px 0 18px;"} -```{ojs} -md`Active element: ${crScrollerName}` -md`Element progress: ${crScrollerProgress}` -``` -::: \ No newline at end of file diff --git a/docs/guide/index.qmd b/docs/guide/index.qmd index 4868ff9..da4d1e0 100644 --- a/docs/guide/index.qmd +++ b/docs/guide/index.qmd @@ -1,3 +1,34 @@ -This is a landing page for a guide. +This is a landing page for a guide. Draft materials added below can later be reorganized into different qmds linked off of the sidebar. + +# Layouts + +The relative positioning of the narrative column and the sticky column determine the *layout*. You can set the layout of a section using the `layout` attribute on the block that defines the section. + +```markdown +:::{.cr-section layout="overlay-center"} +< Content of the section, including stickies and narrative blocks > +::: +``` +Options for `layout` include: + +- `sidebar-left` (default) +- `sidebar-right` +- `overlay-left` +- `overlay-center` +- `overlay-right` + +In the overlay layouts, the sticky column occupies the entire screen width and the narrative column scrolls over it. All layouts will automatically revert to `overlay-center` when viewed on a mobile device. + +Documents containing multiple closeread sections can use the same layouts or different ones. As an alternative to specifying the layout on the section block, it can also be defined in the document yaml under the `cr-section` key. + +```yml +--- +format: + closeread-html: + cr-section: + layout: "overlay-center" +--- +``` +If a layout is specified in the document yaml, it will be propagated to all sections in the document. If a section has its own layout specified, that will override the document yaml similar to how code cell execution options work. \ No newline at end of file diff --git a/docs/reference/index.qmd b/docs/reference/index.qmd index 5078e41..dcd81f5 100644 --- a/docs/reference/index.qmd +++ b/docs/reference/index.qmd @@ -1,55 +1,60 @@ --- title: Reference +toc: true +toc-expand: true +css: ref-styles.css listing: - id: yaml-options template: reference.ejs contents: yaml-options.yml + - id: section-options + template: reference.ejs + contents: section-options.yml + - id: trigger-options + template: reference.ejs + contents: trigger-options.yml - id: sticky-options template: reference.ejs contents: sticky-options.yml - - id: step-options - template: reference.ejs - contents: step-options.yml --- -### Document Metadata +## Document Metadata -Document-wide options under the custom format key in YAML format. - -```yaml -format: - closeread-html -``` +Document-wide options under the `closeread-html` format key in YAML format. :::{#yaml-options} ::: -### Layout Block Options +## Fenced Div Options -Todo. +Closeread takes advantage of the Pandocs's [fenced div syntax](https://quarto.org/docs/authoring/markdown-basics.html#sec-divs-and-spans) to pass options to sections, triggers, and stickies. The fenced div syntax allows you to wrap content (a figure, a paragraph, etc) in a div and pass it one or more identifiers, classes, and attributes. -### Sticky Block Options - -A sticky block is an element tagged with an identifier prefixed with `cr-`. e.g. - -```qmd -:::{#cr-mypara} -Here is a paragraph that will be a sticky block. +```markdown +:::{#myid .myclass myattribute="something"} +< block content like a figure or paragraph > ::: ``` -:::{#sticky-options} +Identifiers start with `#`; classes start with `.`, and attributes have a key / value pair separated by `=` and no spaces. You can assign to a div one or more of each of these (though they must be supplied in identifier-class-attributes order) as long as each is separated by a space. + +### Section Options + +A closeread section is a div with the class `cr-section` that contains stickies and triggers. + +:::{#section-options} ::: -### Step Block Options +### Trigger Block Options -A step block is an element that uses the `focus-on` attribute to trigger effects on a particular sticky block. +A trigger block is an element with the `focus-on` attribute to trigger effects on a particular sticky element. -```qmd -:::{focus-on="cr-mypara"} -Here is a paragraph that will trigger focus on `cr-mypara`. +:::{#trigger-options} ::: -``` -:::{#step-options} + +### Sticky Block Options + +A sticky block is an element tagged with an identifier prefixed with `cr-`. + +:::{#sticky-options} ::: diff --git a/docs/reference/ref-styles.css b/docs/reference/ref-styles.css new file mode 100644 index 0000000..a43d7e0 --- /dev/null +++ b/docs/reference/ref-styles.css @@ -0,0 +1,9 @@ + +dl, dd { + padding-left: 20px; +} + +.options-block { + padding-left: 10px; + padding-bottom: 20px; +} \ No newline at end of file diff --git a/docs/reference/reference.ejs b/docs/reference/reference.ejs index 3c623d6..7a92278 100644 --- a/docs/reference/reference.ejs +++ b/docs/reference/reference.ejs @@ -1,18 +1,36 @@ ```{=html} - ----- - + <% for (const item of items) { %> - - - - - +
<%= item.option %>
+
+

<%= item.description %>

+
+ <% if (item['format']) { %> +
format
+
<%= item['format'] %>
+ <% } %> + <% if (item['parent-key']) { %> +
parent key
+
<%= item['parent-key'] %>
+ <% } %> + <% if (item['possible-values']) { %> +
possible values
+
+
    + <% let index = 0; %> + <% for (const val of item['possible-values']) { %> +
  • + <%= val %> + <% if (index === 0) { %> + (default) + <% } %> +
  • + <% index++; %> + <% } %> +
+
+ <% } %> +
+
<% } %> - -
<%= item.option %>

<%= item['option-type'] %>

<%= item.description %>

``` \ No newline at end of file diff --git a/docs/reference/section-options.yml b/docs/reference/section-options.yml new file mode 100644 index 0000000..d91dbb4 --- /dev/null +++ b/docs/reference/section-options.yml @@ -0,0 +1,11 @@ +- option: "layout" + format: "pandoc attribute" + value-type: "scalar" + possible-values: + - "sidebar-left" + - "sidebar-right" + - "overlay-left" + - "overlay-center" + - "overlay-right" + description: Defines the relative positioning of the narrative and sticky columns. Overrides any layout set in the document metadata. + \ No newline at end of file diff --git a/docs/reference/sticky-options.yml b/docs/reference/sticky-options.yml index 71edc58..7415a87 100644 --- a/docs/reference/sticky-options.yml +++ b/docs/reference/sticky-options.yml @@ -1,4 +1,5 @@ - option: "#cr-*" + format: pandoc identifier option-type: "block id" value-type: "scalar" possible-values: diff --git a/docs/reference/step-options.yml b/docs/reference/trigger-options.yml similarity index 76% rename from docs/reference/step-options.yml rename to docs/reference/trigger-options.yml index 654217a..64cdcfc 100644 --- a/docs/reference/step-options.yml +++ b/docs/reference/trigger-options.yml @@ -1,6 +1,5 @@ - option: "focus-on" - option-type: "block attribute" - value-type: "scalar" + format: "pandoc attribute" possible-values: - "{string}" description: Indicate which sticky block to focus on. Will transition to that block and use it in any focus effects. diff --git a/docs/reference/yaml-options.yml b/docs/reference/yaml-options.yml index 992714c..504b5e6 100644 --- a/docs/reference/yaml-options.yml +++ b/docs/reference/yaml-options.yml @@ -1,7 +1,18 @@ - option: "debug-mode" - option-type: document yaml - value-type: "scalar" + format: yaml + parent-key: "format:closeread-html" possible-values: - - "true" - "false" - description: Toggle debug mode. \ No newline at end of file + - "true" + description: Toggle debug mode. + +- option: "layout" + format: yaml + parent-key: "format:closeread-html:cr-section" + possible-values: + - "sidebar-left" + - "sidebar-right" + - "overlay-left" + - "overlay-center" + - "overlay-right" + description: Define the relative positioning of the narrative and sticky columns in all sections in the document. Is overridden if a section specifies its own layout. \ No newline at end of file