website
render
page
link-to
element?
attribute?
site?
push-path
get-attribute
has-attribute?
get-property
de-url
define/  provide-extensible-element
1 Bootstrap
bootstrap-files
content
navbar
container
2 Grids
row
col
col-1
col-2
col-3
col-4
col-5
col-6
col-7
col-8
col-9
col-10
col-11
col-12
col-sm-4
3 Tabs
nav-tabs
active-nav-item
tab-content
tab-pane
active-tab-pane
tab-nav-link
active-tab-nav-link
tabify
4 Cards
card
5 Buttons
button-primary
button-secondary
button-success
button-danger
button-warning
button-info
button-light
button-dark
button-link
7.5

website

thoughtstem

 (require website) package: website

procedure

(render site #:to to)  void?

  site : (listof page?)
  to : path-string?
#lang racket
 
(require website)
 
(define my-site
  (list
    (page index.html
         (html (body (h1 "Hello World"))))))
 
(render my-site #:to "output")

Rendering my-site out to the output directory.

A "site" is just a list of page structures.

Note that the intended dev experience is that you run $ raco website-preview in your output directory, which launches a browser pointing to a locally running webserver. Then you just refresh the page whenever you rerender your site.

struct

(struct page (name content)
    #:extra-constructor-name make-page)
  name : (list-of string?)
  content : page-content?
A page is an abstraction of a file in a project. For example:

project:

  index.html

  css/

    custom.css

  js/

    custom.js

This project would be represented as a site with three pages:

(define project
 (list
  (page index.html ...)
  (page css/custom.css ...)
  (page js/custom.js ...)))

The actual files would be created later, with a call to render.

Note, although a page struct stores its path as a list of strings. You should make use of the fact that the page constructor is actually a macro that parses the first identifier into a list of strings based on the forward slashes. If you don’t want this behaviour, simply pass in a list directly or an id that does not contain slashes or dots.

This example uses all three:

(define my-css-path (list "css" "custom.css"))
(define project
 (list
  (page (list "index.html") ...)
  (page my-css-path ...)
  (page js/custom.js ...)))

As for content, normally, you’ll use the html generating functions that are reprovided by website from scribble/html/html.

Basically, for (almost) every html tag, there is a constructor function with that name. And for (almost) every attribute, there is an identifier ending with a colon. For example:

(html
  (head
    (title "My Site"))
  (body
    (h1 "My Site")
    (img src: "path-to-some-image" alt: "alt text")
    (div style: (properties
                  background-color: "red"
                  color: "green")
      "I am a weird div tag from the 90s")))

Style attributes can make use of the properties helper which gives a special syntax for constructing css strings.

procedure

(link-to page text)  element?

  page : page?
  text : string?
This constructs an a element? whose href: attribute is constructed from the page’s path and whose content comes from text.

It is convenient if you want to link from one page to another without thinking about the target page’s path. If you decide to change the path later, the links will update accordingly.

Just use an old-fashioned a tag, though, if you want finer-grained control or if you don’t have the target page in scope.

value

element? : (or/c procedure? outputtable/c)

Should be an outputtable/c returned by one of the html generating functions: e.g. html, head, body, p, h1, etc...

Or it can be a procedure that would return such a thing. This can be useful in cases like write-img, which will write out an image file at render time, and will return an img element? with the appropriate src: attribute.

value

attribute? : symbol?

A symbol that ends in a colon. When passed into a element constructor, it must be followed by a value. This becomes the value on the rendered html element.

(h1 id: "title" "HI")

Becomes:

<h1 id="title">HI</h1>

value

site? : (listof page?)

Yep, a site is just a list.

Joining two sites together is as simple as using append.

procedure

(push-path part page)  page?

  part : string?
  page : (or/c page? (listof page?))
Adds the part onto the page’s path. If the supplied page is actually a list, push-path maps itself across them to push them all down a (virtual) directory. Makes it easy to include pages as a sub-site within an existing site, where the subsite is served at a fixed subpath.

procedure

(get-attribute attr: element)  any/c

  attr: : symbol?
  element : element?
Gets the attribute from the element.

(get-attribute id: (div id: "id"))

Gives "id".

Fails if it doesn’t exist.

procedure

(has-attribute? attr: element)  boolean?

  attr: : symbol?
  element : element?
Returns true if the element has the attribute.

procedure

(get-property prop: style)  string?

  prop: : symbol?
  style : string?
This returns the property within a style string:

(get-property color:
  (get-attribute style:
    (div style: (properties color: "red"))))

Gives you "red".

procedure

(de-url string-with-url)  string?

  string-with-url : string?
Takes strings like "url(http://example.com/img.png)" and gives you back just the url, like "http://example.com/img.png".

syntax

(define/provide-extensible-element name base overrides ...)

(define/provide-extensible-element
  page-item
  li
  (class: "page-item" class-join))

1 Bootstrap

 (require website/bootstrap) package: website

This provides features for building websites that use bootstrap’s visual vocabularly.

Here’s a (simplified) example from a version of the metacoders.org website (which is built on top of website/bootstrap).

It shows how to construct an index page and three content pages, each connected by links on a bootstrap navbar.

#lang racket
 
(require website/bootstrap)
 
(define index-path       (list "index.html"))
(define city-search-path (list "city-search.html"))
(define learn-more-path  (list "learn-more.html"))
(define get-to-work-path (list "get-to-work.html"))
 
(define my-nav
  (navbar
    #:brand "MetaCoders"
    (nav-link learn-more-path  "Learn More")
    (nav-link city-search-path "Enroll Kids")
    (nav-link get-to-work-path "Get To Work")))
 
(define (normal-content . more)
  (content
    my-nav
    (container more)))
 
(define index
  (page index-path
        (normal-content
          (h1 "Index"))))
 
(define learn-more
  (page learn-more-path
        (normal-content
          (h1 "Learn More"))))
 
(define city-search
  (page city-search-path
        (normal-content
          (h1 "City Search"))))
 
(define get-to-work
  (page get-to-work-path
        (normal-content
          (h1 "Get To Work"))))
 
(define my-site
 (append
  (list
   index
   learn-more
   city-search
   get-to-work)
  (bootstrap-files)))
 
(render my-site #:to "out")

procedure

(bootstrap-files)  site?

Appending this in your site adds in the files:

js/

  jquery-3.2.1.slim.min.js

  bootstrap.bundle.min.js

css/

  bootstrap.min.css

Note that it’s still up to you to actually include these files in the html pages of your site. For that, you would use include-bootstrap-js usually at the end of the (body ...) portion of a page, include-bootstrap-css usually in the (head ...) portion of a page, or use content to automatically do both.

procedure

(content #:head head body-content ...)  element?

  head : element?
  body-content : element?
Returns a html element with the bootstrap css and js included in the traditional head and body locations respectively.

Use this as the basic building block for any bootstrap-enabled page in your bootstrap-enabled site. (Don’t forget to append the (bootstrap-files) to your site before rendering)

procedure

(navbar #:brand brand content ...)  element?

  brand : element?
  content : element?
Returns a bootstrap navbar element. The content is placed inside a ul tag (and nav-link returns a li item). So you add elements to a bootstrap page as follows:

(content
  (navbar #:brand "My Site"
    (nav-link "about.html" "About")))

procedure

(container content ...)  element?

  content : element?
Wraps the given content in a div whose class: is "container".

2 Grids

Bootstraps grid system is what allows for responsive design. Constructors are provided for row and columns of various sizes – all of which correspond to Bootstrap classes in a straight-forward way.

procedure

(row #:element element content)  element?

  element : div
  content : (or/c element? attribute?)
A wrapper for column elements. A full example for context:

#lang racket
 
(require website/bootstrap)
 
(define (normal-content title . stuff)
 (content
  (container
   (h1 title)
   stuff)))
 
(define my-site
 (append
  (bootstrap-files)
  (list
   (page index.html
    (normal-content
     "Column Demos"
 
     (row
      (col style: "background-color: red"  "1")
      (col-6 style: "background-color: green" "2")
      (col style: "background-color: blue" "3"))
 
     (row
      (col style: "background-color: red"  "1")
      (col-5 style: "background-color: green" "2")
      (col style: "background-color: blue" "3"))
 
 
     (row
      (col-6 class: "col-md-4" style: "background-color: red"  "1")
      (col-6 class: "col-md-4" style: "background-color: green"  "1")
      (col-6 class: "col-md-4" style: "background-color: blue"  "1")))))))
 
(render my-site #:to "out")

procedure

(col #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-1 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-2 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-3 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-4 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-5 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-6 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-7 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-8 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-9 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-10 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-11 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(col-12 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

Also available (but I am too lazy to list them) are the variants with sm, md, lg, or xl.

Once such example:

procedure

(col-sm-4 #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

3 Tabs

procedure

(nav-tabs #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(active-nav-item #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(tab-content #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(tab-pane #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(active-tab-pane #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(tab-nav-link #:element element content)  element?

  element : div
  content : (or/c element? attribute?)

procedure

(active-tab-nav-link #:element element    
  content)  element?
  element : div
  content : (or/c element? attribute?)

procedure

(tabify content ...)  element?

  content : any/c
Sorts any nav-link items from any tab-pane items. Constructs the appropriate HTML structure for (single-page) tabbed navigation of content.

The hrefs (for nav-link items) and ids (for tab-pane items) must match, as would be the case if you were constructing your Bootstrap HTML by hand.

For example, here are four tabs with four panes.

(tabify (active-tab-nav-link href: "#s-g" "Story to Game") (tab-nav-link href: "#g-s" "Game to Story") (tab-nav-link href: "#g-g" "Game to Game") (tab-nav-link href: "#s-s" "Story to Story") (active-tab-pane id: "s-g" (h3 "Story to Game")) (tab-pane id: "g-s" (h3 "Game to Story")) (tab-pane id: "g-g" (h3 "Game to Game")) (tab-pane id: "s-s" (h3 "Story to Story")))

4 Cards

Bootstrap’s cards are a key aspect of Bootstrap’s visual language.

procedure

(card #:element element content)  element?

  element : div
  content : (or/c element? attribute?)
This is a psuedo element – a div with the class: "card"

Things that can be nested inside of it: card-img-top, card-body, card-title, card-subtitle, card-text, and card-link.

Use these as building blocks to make your own sorts of cards.

Examples:

(card
  (card-img-top)
  (card-body
    (card-title "I am a card")
    (card-subtitle "with a subtitle")
    (card-text "Lorem ipsum ....")
    (button-primary
      "Learn More")))

5 Buttons

There are a variety of constructors for common Bootstrap buttons.

Each is implemented as a psuedo element, meaning that although they return button values, you can still pass in content and attributes as you would with any html element constructors.

procedure

(button-primary #:element element content)  element?

  element : button?
  content : (or/c element? attribute?)
(button-primary
   id: "main-button"
   "My Button")

procedure

(button-secondary #:element element    
  content)  element?
  element : button?
  content : (or/c element? attribute?)

procedure

(button-success #:element element content)  element?

  element : button?
  content : (or/c element? attribute?)

procedure

(button-danger #:element element content)  element?

  element : button?
  content : (or/c element? attribute?)

procedure

(button-warning #:element element content)  element?

  element : button?
  content : (or/c element? attribute?)

procedure

(button-info #:element element content)  element?

  element : button?
  content : (or/c element? attribute?)

procedure

(button-light #:element element content)  element?

  element : button?
  content : (or/c element? attribute?)

procedure

(button-dark #:element element content)  element?

  element : button?
  content : (or/c element? attribute?)

procedure

(button-link #:element element content)  element?

  element : button?
  content : (or/c element? attribute?)