2023-07-18 16:46:44 +02:00
;; The Bootstrap example 'Album' demonstrates how to create a responsive,
;; mobile-first photo album layout using Bootstrap's grid system, cards, and
;; some other components. Here's a brief overview of its structure:
;; Navigation Bar: At the top, there's a navbar with the site name and some
;; links. The navbar collapses into a hamburger menu on smaller screens for
;; better mobile usability.
;; Hero Unit: The main headline or 'Hero unit' is a large callout with some
;; text and a call-to-action button. It's a marketing section of the page that
;; offers users a brief overview of the product's main feature.
;; Photo Album: The main content of the page is a photo album. This is
;; implemented as a responsive grid of card components. Each card contains an
;; image, some text, and a button. The grid adjusts from a single column on the
;; smallest screens to multi-column layouts on larger screens.
;; Footer: Finally, there's a footer at the bottom of the page with some links
;; and copyright information.
;; The Album example is a good demonstration of how Bootstrap can be used to
;; create a responsive, mobile-first layout with relatively little code. It
;; shows how to use some of the most common Bootstrap components, like navbars,
;; cards, and the grid system, and it provides a solid basis for further
;; customization and extension.
;; It's worth noting that while the layout is quite simple, it can be used as a
;; starting point for more complex projects. By understanding and working
;; through this example, one can get a grasp of Bootstrap's conventions and
;; learn how to use its components effectively.
2023-07-17 21:50:42 +02:00
( defpackage cl-sbt/album
2023-07-15 21:09:40 +02:00
( :use :cl )
2023-07-17 21:50:42 +02:00
( :import-from :cl-sbt/grid :con :row :col )
2023-07-18 19:58:32 +02:00
( :import-from :cl-sbt/navbar :navbar :brand :toggler :collapsible )
2023-07-16 13:58:34 +02:00
( :import-from :cl-sbt :write-string-to-file :with-page )
2023-07-16 11:54:23 +02:00
( :import-from :spinneret :with-html-string )
2023-07-15 21:09:40 +02:00
( :export
2023-07-16 14:10:31 +02:00
:contact
:about
:header
:footer
2023-07-18 16:46:44 +02:00
:album
2023-07-16 14:10:31 +02:00
:*navbar-header-id* )
( :documentation " The ` cl-sbt-album ` package provides macros for building an
album-style website page using Bootstrap and Spinneret. " ) )
2023-07-15 21:09:40 +02:00
2023-07-17 21:50:42 +02:00
( in-package :cl-sbt/album )
2023-07-15 21:09:40 +02:00
2023-07-15 22:36:49 +02:00
( defvar *navbar-header-id* "navbarHeader" )
2023-07-15 21:09:40 +02:00
2023-07-16 12:11:42 +02:00
( defmacro about ( ( &key ( textbody "secondary" ) ) &body body )
2023-07-16 14:10:31 +02:00
" Generates an HTML 'About ' section with the provided content.
TEXTBODY: Specifies the color scheme of the text body. Default is 'secondary '.
BODY: Specifies the HTML content to be included in the 'About ' section. This
can be any valid HTML content that spinneret:with-html can parse.
Example usage:
( about ( ) \"This is an about section.\" )
; This will generate an 'About' section with secondary color text and the
; provided content."
2023-07-15 21:09:40 +02:00
` ( spinneret:with-html
( :h4 "About"
2023-07-16 12:11:42 +02:00
( :p :class , ( concatenate 'string
( if textbody ( format nil "text-body-~a" textbody ) "" ) )
2023-07-15 21:09:40 +02:00
,@ body ) ) ) )
( defmacro contact ( &rest rest )
2023-07-16 14:10:31 +02:00
" Generates an HTML 'Contact ' section with the provided links.
REST: A list of plists, each representing a link. Each plist should contain a
:url and a :label.
Example usage:
( contact ( :url \"#\" :label \"Follow on Twitter\" )
( :url \"#\" :label \"Like on Facebook\" )
( :url \"#\" :label \"Email me\" ) )
; This will generate a 'Contact' section with three links, each with the
; provided URL and label."
2023-07-15 21:09:40 +02:00
` ( spinneret:with-html
( :h4 "Contact" )
( :ul :class "list-unstyled"
,@ ( loop for item in rest
collect ( destructuring-bind ( &key url label ) item
` ( :li ( :a :class "text-white"
:href , url
, label ) ) ) ) ) ) )
2023-07-18 16:56:00 +02:00
( defmacro navigation ( &body body )
2023-07-16 13:58:34 +02:00
" Generates an HTML header for the album page.
This macro generates a header with a navigation bar and some predefined
content, including an 'About ' section with a description of the album and a
'Contact ' section with some dummy contact links.
Additional content can be added to the header by passing it as BODY arguments
to the macro. The BODY content will be included in the header after the
predefined content. "
2023-07-15 21:09:40 +02:00
` ( spinneret:with-html
2023-07-18 19:58:32 +02:00
( :header ( collapsible , *navbar-header-id*
2023-07-18 20:09:19 +02:00
,@ body )
2023-07-16 13:58:34 +02:00
( navbar ( :fluid nil )
( brand ( ) "Album" )
2023-07-18 20:09:19 +02:00
( toggler , *navbar-header-id* ) ) ) ) )
2023-07-15 21:09:40 +02:00
2023-07-18 19:58:32 +02:00
( defmacro footer ( ( &key ( color ' ( :text :body-secondary ) )
( spacing ' ( :property :p :size 5 ) ) ) &body body )
2023-07-16 10:28:22 +02:00
" Generates an HTML footer with Bootstrap classes.
2023-07-17 21:50:42 +02:00
COLOR: Specifies the color scheme of the footer. It 's a list containing keyword
arguments that can be passed to the cl-sbt/utility:color function.
2023-07-16 10:28:22 +02:00
SPACING: A list specifying the Bootstrap spacing class. The list should contain
2023-07-17 21:50:42 +02:00
keyword arguments that can be passed to the cl-sbt/utility:spacing function.
2023-07-16 10:28:22 +02:00
2023-07-16 11:36:58 +02:00
BODY: Optional. Specifies additional HTML content to be added to the footer.
This can be any valid HTML content that spinneret:with-html can parse.
2023-07-16 10:28:22 +02:00
The footer generated contains fixed content, including a 'Back to top ' link and
a short paragraph about Bootstrap.
Example usage:
2023-07-17 21:50:42 +02:00
( footer ( :color ( :text :primary :background :light )
:spacing ( :property :p :side :y :size 4 ) )
2023-07-16 11:36:58 +02:00
( :p :class \"custom-class\" \"Custom content here\" ) )
2023-07-17 21:50:42 +02:00
; This will generate a footer with primary color text and light background
; with a top/bottom padding of size 4. Additionally, a paragraph with class
; 'custom-class' and text 'Custom content here' will be added to the footer."
2023-07-15 21:09:40 +02:00
` ( spinneret:with-html
2023-07-16 10:28:22 +02:00
( :footer :class , ( concatenate 'string
2023-07-17 21:50:42 +02:00
( if ( null color ) ""
( apply #' cl-sbt/utility:color color ) )
2023-07-16 10:28:22 +02:00
( if ( null spacing ) ""
2023-07-17 12:54:03 +02:00
( apply #' cl-sbt/utility:spacing spacing ) ) )
2023-07-18 19:58:32 +02:00
( con ( )
( :p :class "float-end mb-1"
( :a :href "#" "Back to top" ) )
( :p :class "mb-1"
"Album example is © Bootstrap, but please download and customize it for yourself!" )
( :p :class "mb-0"
"New to Bootstrap? "
( :a :href "/" "Visit the homepage" )
" or read our "
( :a :href "/docs/5.3/getting-started/introduction/" "getting started guide" ) )
,@ body ) ) ) )
2023-07-15 21:09:40 +02:00
2023-07-18 16:56:00 +02:00
( defmacro hero ( &body body )
2023-07-18 16:46:13 +02:00
` ( spinneret:with-html
( con ( :spacing ( :property :p :size 5 ) )
( row ( :spacing ( :property :p :side :lg :size 5 ) )
( col ( :breakpoint ( :kind :col :md ( 8 nil ) :lg ( 6 nil ) )
:spacing ( :property :p :size 5 ) )
2023-07-18 16:56:00 +02:00
,@ body ) ) ) ) )
2023-07-18 16:46:13 +02:00
2023-07-18 16:56:00 +02:00
( defmacro page ( title &body body )
2023-07-16 13:58:34 +02:00
` ( with-page ( :title , title )
2023-07-18 20:09:19 +02:00
( navigation
( col ( :breakpoint ( :kind :col :sm ( 8 nil ) :md ( 7 nil ) )
:spacing ( :property :p :side :y :size 4 ) )
( about ( ) "Add some information about the album below, the author, or any other background context. Make it a few sentences long so folks can pick up some informative tidbits. Then, link them off to some social networking sites or contact information." ) )
( col ( :breakpoint ( :kind :col :sm ( 8 nil ) :md ( nil 1 ) ) )
( contact ( :url "#" :label "Follow on Twitter" )
( :url "#" :label "Like on Facebook" )
( :url "#" :label "Email me" ) ) ) )
2023-07-16 14:10:31 +02:00
( :main ,@ body )
2023-07-18 19:06:38 +02:00
( footer ( ) ) ) )
2023-07-18 16:46:13 +02:00
( defun write-album ( &key ( lang "de" ) ( style :tree ) ( fc 120 ) )
( let ( ( spinneret:*html-lang* lang )
( spinneret:*html-style* style )
( spinneret:*fill-column* fc ) )
( write-string-to-file "album.html"
2023-07-18 16:56:00 +02:00
( with-html-string ( page "Album" ( hero ) ) ) ) ) )