├── .gitignore ├── README.md ├── cols.css ├── cols_macro.js ├── demo.Rmd ├── demo.html ├── eee-fonts.css ├── eee.css ├── libs └── remark-css │ └── default.css └── xaringan_columns.Rproj /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Create Columns for Remark.js / xaringan 2 | 3 | This project contains a small JavaScript macro which can be used in Markdown for writing Remark.js or xaringan slides. The purpose of the macro is to allow users to easily create multiple-column slide layout. 4 | 5 | ## Contents 6 | 7 | The most important documents you will find here are: 8 | 9 | - `cols_macro.js`: contains actual `col_header`, `col_list`, and `col_row` macro code 10 | - `cols.css`: contains corresponding macro CSS - part **necessary** to make the macro work and part illustrating *optional* settings (e.g. coloring) 11 | - `demo.Rmd`: shows where and how to call these macros in a .Rmd file for a [`xaringan`](https://github.com/yihui/xaringan) presentation 12 | - `demo.html`: shows the output. It is also hosted on the corresponding [GitHub Pages](https://emilyriederer.github.io/xaringan_columns/demo.html) 13 | 14 | Other style sheets are simple are for personal design choices in my demo deck. 15 | 16 | ## How-To Use 17 | 18 | To use this macro, you would need to save `cols_macro.js` and `cols.css` in your project's directory. Then, reference these files in your YAML header. An example of this step is the YAML header of `demo.Rmd`: 19 | 20 | ```markdown 21 | --- 22 | title: "Macro for Many Columns" 23 | author: "Emily Riederer" 24 | output: 25 | xaringan::moon_reader: 26 | lib_dir: libs 27 | css: ["default", "eee.css", "eee-fonts.css", "cols.css", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"] 28 | nature: 29 | beforeInit: "cols_macro.js" 30 | highlightStyle: github 31 | highlightLines: true 32 | countIncrementalSlides: false 33 | --- 34 | ``` 35 | 36 | Observe that `cols.css` is in the `css:` key-value pair, and `cols_macro.js` is in the `beforeInit` key-value pair. 37 | 38 | ## Functionality 39 | 40 | `cols_macro.js` defines three macros that can be called in Markdown as follows: 41 | 42 | - `![:col_header x, y, z]`: creates header with CSS class `multicolhead` for styling 43 | - `![:col_list x, y, z]`: creates row of bulletted list items for each column 44 | - `![:col_row x, y, z]`: creates row of text for each column 45 | 46 | To create a three-column layout with a header row and lists, you might do something like this: 47 | 48 | ```markdown 49 | ![:col_header Header 1, Header 2, Header 3] 50 | ![:col_list item 1.1, item 2.1, item 3.1] 51 | ![:col_list item 1.2, item 2.2, item 3.2] 52 | ``` 53 | where you would replace all "Header i" and "item i" with whatever text you want. 54 | 55 | ## A Note on Design 56 | 57 | This approach lets you build your columns row-by-row, similar to how you might think about laying things out in your head. As a result, it's implemented with a bunch `div`s. I considered alternatively having a single syntax with something like: 58 | 59 | ``` 60 | ![:col_table dim1, dim2, 61 | h1, h2, h3, 62 | item 1.1, item 1.2, item 1.3] 63 | ``` 64 | 65 | which could be implemented with `` instead. However, the end-user syntax here seemed bulkier to me, and it seemed less in line with how I tend to be thinking and writing when I'm making this sort of layout, so I took with the former approach. 66 | 67 | ## Benefits 68 | 69 | My motivation for making this was that I'm trying to switch most of my personal/professional work to [`xaringan`](https://github.com/yihui/xaringan) and away from editors like Powerpoint. I knew to use this at work, someone was going to inevitably want a "three boxer", and I couldn't find a great existing way to do that. 70 | 71 | Relative to just laying things out in rows, columns can sometimes have advtanges in terms of emphasizing the parallel (versus sequential) nature of content. 72 | 73 | Relative to hard-coding an HTML table in my RMarkdown, this keeps my RMarkdown a lot more readable/writable by modularizing the code. Now, I don't have to be retyping/copying the same thing over and over or risk accidentally deleting something in a chunk of HTML code. Unfortunately, this means I do sacrifice something in the abilitiy for truly arbitrary custom styling. 74 | 75 | ## Pitfalls 76 | 77 | This is currently in total hack status as something I made quickly just to achieve a specific end results for a deck I was making. It worked fine for my purpose, but undoubtedly has rough edges. 78 | 79 | There are currently a number of known short-comings to this approach. I list them below, but they are better understoof via illustration in the [demo deck](https://emilyriederer.github.io/xaringan_columns/demo.html). Known issues are: 80 | 81 | - It can be hard to leave one column truly blank for a given row 82 | - Text in any cell can't contain commas or periods 83 | 84 | ## About Macros for `xaringan` 85 | 86 | This is a section I'm definitely not the right person to write. My inspiration/learning started from the [`xaringan` GitHub issue](https://github.com/yihui/xaringan/issues/80) on the topic. 87 | -------------------------------------------------------------------------------- /cols.css: -------------------------------------------------------------------------------- 1 | /* Required: Clear floats after the columns */ 2 | .multicolrow:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | 8 | /*Optional: Color header boxes*/ 9 | .multicolhead { 10 | background-color:#4B9CD3; 11 | color: #FFFFFF; 12 | } 13 | 14 | .wordwrap { 15 | white-space: pre-wrap; /* CSS3 */ 16 | white-space: -moz-pre-wrap; /* Firefox */ 17 | white-space: -o-pre-wrap; /* Opera 7 */ 18 | word-wrap: break-word; /* IE */ 19 | } 20 | 21 | .multicolcol { 22 | min-height: 1px; 23 | } 24 | -------------------------------------------------------------------------------- /cols_macro.js: -------------------------------------------------------------------------------- 1 | // creates equal-width column headers with css class multicolhead for styling 2 | // ...titles is a comma-separated list a values to determine number and contents of columns 3 | 4 | remark.macros.col_header = function (...titles) { 5 | 6 | var num_items = titles.length; 7 | var width = Math.floor(100/num_items) - 2*0.5; 8 | 9 | var code = 10 | '
'+ 11 | '
'; 13 | 14 | for(let i = 0; i < num_items; i++) 15 | code = 16 | code + 17 | '
' + 25 | titles[i] + 26 | '
'; 27 | 28 | 29 | code = code + 30 | '
' + 31 | '
'; 32 | 33 | return code; 34 | 35 | }; 36 | 37 | // creates row of equal-width column list elements 38 | // ...bodies is a comma-separated list a values to determine number and contents of columns 39 | 40 | remark.macros.col_list = function (...bodies) { 41 | 42 | var num_items = bodies.length; 43 | var width = Math.floor(100/num_items) - 0.5; 44 | 45 | var code = 46 | '
'+ 47 | '
' ; 48 | 49 | for(let i = 0; i
  • ' + 57 | bodies[i] + 58 | '
  • '; 59 | 60 | code = code + 61 | '' + 62 | '
    '; 63 | 64 | return code; 65 | 66 | }; 67 | 68 | // creates row of equal-width column contents 69 | // ...bodies is a comma-separated list a values to determine number and contents of columns 70 | 71 | remark.macros.col_row = function (...bodies) { 72 | 73 | var num_items = bodies.length; 74 | var width = Math.floor(100/num_items) - 0.5; 75 | 76 | var code = 77 | '
    '+ 78 | '
    ' ; 79 | 80 | for(let i = 0; i' + 88 | bodies[i] + 89 | '
    '; 90 | 91 | code = code + 92 | '' + 93 | '
    '; 94 | 95 | return code; 96 | 97 | }; 98 | -------------------------------------------------------------------------------- /demo.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Macro for Many Columns" 3 | author: "Emily Riederer" 4 | output: 5 | xaringan::moon_reader: 6 | lib_dir: libs 7 | css: ["default", "eee.css", "eee-fonts.css", "cols.css", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"] 8 | nature: 9 | beforeInit: "cols_macro.js" 10 | highlightStyle: github 11 | highlightLines: true 12 | countIncrementalSlides: false 13 | --- 14 | 15 | ```{r setup, include=FALSE} 16 | options(htmltools.dir.version = FALSE) 17 | ``` 18 | 19 | 20 | ## Splitting slides into two columns is already pretty easy 21 | 22 | .pull-left[.full-width[ 23 | 24 | You can just write: 25 | 26 | `.pull-left[.full-width[]]` 27 | 28 | for content to go on the left-hand side 29 | 30 | ]] 31 | 32 | 33 | 34 | .pull-right[.full-width[ 35 | 36 | ...and similarly write: 37 | 38 | `.pull-right[.full-width[]]` 39 | 40 | for contents to go on the right-hand side 41 | 42 | ]] 43 | 44 | 45 | --- 46 | 47 | class: inverse, center, middle 48 | 49 | ## But what if you want more than two columns? 50 | 51 | --- 52 | 53 | class: inverse, center, middle 54 | 55 | ## It's macro time! 56 | 57 | --- 58 | 59 | ## Basic idea is to create column structure row-by-row 60 | 61 | `![:col_header x, y, z]`: creates header with CSS class `multicolhead` for styling 62 | 63 | `![:col_list x, y, z]`: creates row of bulletted list items for each column 64 | 65 | `![:col_row x, y, z]`: creates row of text for each column 66 | 67 | --- 68 | 69 | ## For example, writing this... 70 | 71 | ````html 72 | ![:col_header Header 1, Header 2, Header 3] 73 | ![:col_list You can add rows with bulletted lists, item2, item3] 74 | ![:col_list item4, item5, Or normal chunks of text (see below)] 75 | ![:col_row 76 | This is a long descriptive paragraph about a point 77 | that I need to explain in great details for it to 78 | be fully understood, 79 | 80 | This is another long explanatory piece. Sometimes you need 81 | to provide you readers a lot of information, 82 | 83 | Testing testing 1 2 3. Let's make this a little longer] 84 | ```` 85 | --- 86 | 87 | ## Three column layouts are easy 88 | 89 | ![:col_header Header 1, Header 2, Header 3] 90 | ![:col_list You can add rows with bulletted lists, item2, item3] 91 | ![:col_list item4, item5, Or normal chunks of text (see below)] 92 | ![:col_row 93 | This is a long descriptive paragraph about a point that I need to explain in great details 94 | for it to be fully understood, 95 | 96 | This is another long explanatory piece. Sometimes you need to provide you readers a lot 97 | of information, 98 | 99 | Testing testing 1 2 3. Let's make this a little longer] 100 | 101 | --- 102 | 103 | ## Or four! 104 | 105 | ![:col_header Header 1, Header 2, Header 3, Header 4] 106 | ![:col_list You can add rows with bulletted lists, item2, item3, hello] 107 | ![:col_list item4, item5, Or normal chunks of text (see below), la la la] 108 | ![:col_row 109 | This is a long descriptive paragraph about a point that I need to explain in great details 110 | for it to be fully understood, 111 | 112 | This is another long explanatory piece. Sometimes you need to provide you readers a lot 113 | of information, 114 | 115 | Testing testing 1 2 3, 116 | 117 | I'm in the fourth column] 118 | 119 | --- 120 | 121 | ## Or five! 122 | 123 | ### But it starts to get crowded... 124 | 125 | ![:col_header Header 1, Header 2, Header 3, Header 4, Header 5] 126 | ![:col_list You can add rows with bulletted lists, item2, item3, hello, more content] 127 | ![:col_list item4, item5, Or normal chunks of text (see below), la la la, tra la la] 128 | ![:col_row 129 | More text, 130 | More text, 131 | ., 132 | More text, 133 | More text 134 | ] 135 | 136 | --- 137 | 138 | ## You can also use with HTML vs text 139 | 140 | ````html 141 | ![:col_header 142 | , 143 | , 144 | 145 | ] 146 | ![:col_row 147 | My first paragraph of content1, 148 | This is more content2, 149 | Hello my name is content3] 150 | ```` 151 | 152 | turns into this: 153 | 154 | ![:col_header 155 | , 156 | , 157 | 158 | ] 159 | ![:col_row 160 | My first paragraph of content1, 161 | This is more content2, 162 | Hello my name is content3] 163 | 164 | --- 165 | 166 | ## And you don't have to have headers if you don't want to 167 | 168 | ![:col_row 169 | 170 | Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua, 171 | 172 | Ut enim ad minim veniam quis nostrud; exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat, 173 | 174 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur, 175 | 176 | Excepteur sint occaecat cupidatat non proident sunt in culpa qui officia deserunt mollit anim id est laborum 177 | 178 | ] 179 | 180 | --- 181 | 182 | ## It's somewhat flexible 183 | 184 | ### `col_header` adjusts to tallest needed box 185 | 186 | ![:col_header Super Long Headers Are Increase Height of All Headers, Header 2, Header 3] 187 | ![:col_list You can add rows with bulletted lists, item2, item3] 188 | ![:col_list item4, item5, Or normal chunks of text (see below)] 189 | 190 | --- 191 | 192 | ## It's somewhat flexible 193 | 194 | ### Super long words will wrap 195 | 196 | ![:col_header Header 1, Thisisalloneveryveryverylongword, Header 3] 197 | ![:col_list You can add rows with bulletted lists, item2, item3] 198 | ![:col_list item4, item5, Or normal chunks of text (see below)] 199 | 200 | But still not the best look... 201 | 202 | --- 203 | 204 | ## It's not perfect 205 | 206 | ### Here's how to make a totally blank cell 207 | 208 | ````html 209 | ![:col_header Header 1, Header 2, Header 3] 210 | ![:col_row content1, , content3] 211 | ```` 212 | 213 | ![:col_header Header 1, Header 2, Header 3] 214 | ![:col_row content1, , content3] 215 | 216 | --- 217 | 218 | ## It's not perfect 219 | 220 | ### Commas separate columns so can't be used within a column. 221 | 222 | Instead, one can use the unicode character `,`. 223 | 224 | ````html 225 | ![:col_header Header 1, Header 2, Header 3] 226 | ![:col_row I wish this would be in column 1, and this would also be in column 1, ,] 227 | ```` 228 | 229 | doesn't honor the blank: 230 | 231 | ![:col_header Header 1 , Header 2 , Header 3] 232 | ![:col_row I wish this would be in column 1, and this would also be in column 1, ,] 233 | 234 | ...and using periods just flat out makes it error out 235 | 236 | --- 237 | 238 | ## But for now it gets the job done 239 | 240 | I like it better than current alternatives 241 | 242 | ![:col_header Just Using Rows, Raw HTML in Markdown, Macro] 243 | ![:col_row Pro, Pro, Pro] 244 | ![:col_list 245 | Easy to implement without custom scripting, 246 | Flexibility for custom styling, 247 | Cleaner syntax is docs 248 | ] 249 | ![:col_row Con, Con, Con] 250 | ![:col_list 251 | Makes info seem sequential vs parallel, 252 | Bulky to leave in code, 253 | Limitations described before 254 | ] 255 | ![:col_list 256 | One option could get boring, 257 | More typo-prone and less repeatable, 258 | 259 | ] 260 | 261 | --- 262 | 263 | ## Ideas or suggestions? 264 | 265 | Please get in touch! 266 | 267 | 268 | https://github.com/emilyriederer/xaringan_columns 269 | 270 | 271 | [@emilyriederer](https://twitter.com/EmilyRiederer) -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Macro for Many Columns 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 279 | 280 | 281 | 282 | 369 | 370 | 389 | 390 | 400 | 401 | 402 | -------------------------------------------------------------------------------- /eee-fonts.css: -------------------------------------------------------------------------------- 1 | /* 2 | Non-code fonts chosen per UNC Chapel Hill typography style guide (https://identity.unc.edu/typography/) 3 | */ 4 | 5 | @import url(https://fonts.googleapis.com/css?family=Open+Sans); 6 | @import url(https://fonts.googleapis.com/css?family=Libre+Baskerville); 7 | @import url('https://fonts.googleapis.com/css?family=Inconsolata:400,700'); 8 | 9 | body, h1, h2, h3 { 10 | font-family: 'Open Sans', sans-serif; 11 | } 12 | 13 | .remark-code, .remark-inline-code { 14 | font-family: 'Inconsolata', 'Lucida Console', Monaco, monospace; 15 | color: #13294B 16 | } -------------------------------------------------------------------------------- /eee.css: -------------------------------------------------------------------------------- 1 | /* 2 | Colors chosen per UNC Chapel Hill typography style guide (https://identity.unc.edu/colors/) 3 | */ 4 | 5 | h1, h2, h3 { 6 | font-weight: normal; 7 | color: #4B9CD3; 8 | } 9 | a, a > code { 10 | color: #007FAE; 11 | } 12 | .remark-code-line-highlighted { background-color: rgba(225, 225, 225, 0.3); } 13 | 14 | .inverse { 15 | background-color: #4B9CD3; 16 | } 17 | 18 | .inverse, .inverse h1, .inverse h2, .inverse h3, .inverse a, .inverse a > code { 19 | color: #FFFFFF; 20 | } 21 | 22 | .inverse .remark-slide-number { 23 | display: none; 24 | } -------------------------------------------------------------------------------- /libs/remark-css/default.css: -------------------------------------------------------------------------------- 1 | a, a > code { 2 | color: rgb(249, 38, 114); 3 | text-decoration: none; 4 | } 5 | .footnote { 6 | position: absolute; 7 | bottom: 3em; 8 | padding-right: 4em; 9 | font-size: 90%; 10 | } 11 | .remark-code-line-highlighted { background-color: #ffff88; } 12 | 13 | .inverse { 14 | background-color: #272822; 15 | color: #d6d6d6; 16 | text-shadow: 0 0 20px #333; 17 | } 18 | .inverse h1, .inverse h2, .inverse h3 { 19 | color: #f3f3f3; 20 | } 21 | /* Two-column layout */ 22 | .left-column { 23 | color: #777; 24 | width: 20%; 25 | height: 92%; 26 | float: left; 27 | } 28 | .left-column h2:last-of-type, .left-column h3:last-child { 29 | color: #000; 30 | } 31 | .right-column { 32 | width: 75%; 33 | float: right; 34 | padding-top: 1em; 35 | } 36 | .pull-left { 37 | float: left; 38 | width: 47%; 39 | } 40 | .pull-right { 41 | float: right; 42 | width: 47%; 43 | } 44 | .pull-right + * { 45 | clear: both; 46 | } 47 | img, video, iframe { 48 | max-width: 100%; 49 | } 50 | blockquote { 51 | border-left: solid 5px lightgray; 52 | padding-left: 1em; 53 | } 54 | .remark-slide table { 55 | margin: auto; 56 | border-top: 1px solid #666; 57 | border-bottom: 1px solid #666; 58 | } 59 | .remark-slide table thead th { border-bottom: 1px solid #ddd; } 60 | th, td { padding: 5px; } 61 | .remark-slide thead, .remark-slide tfoot, .remark-slide tr:nth-child(even) { background: #eee } 62 | 63 | @page { margin: 0; } 64 | @media print { 65 | .remark-slide-scaler { 66 | width: 100% !important; 67 | height: 100% !important; 68 | transform: scale(1) !important; 69 | top: 0 !important; 70 | left: 0 !important; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /xaringan_columns.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | --------------------------------------------------------------------------------