ReedyBear's Blog

code for bearblog book theme

CSS Code (dashboard -> theme)

:root {
	--book_width: clamp(100px, 134.38vh , min(100vw, 1000px));
	--max_book_height: min( 100vh, calc( var(--book_width)  / 1.3438));
	--book_height: clamp(100px, 100vh, var(--max_book_height));

	--header_height: 200px;

	--left-arrow-space: calc( var(--book_width) * 0.035) ;
	--right-arrow-space: calc( var(--book_width) * 0.045) ;
	--right-pad: calc(var(--left-arrow-space) + 3.5vh);
	--left-pad: calc(var(--left-arrow-space) + 2.5vh);


	/** distance from the edge of the page? */
	--arrow-margin: calc((100vw - var(--book_width))/2);
	--arrow-margin: 0px;

	--arrow_font_size: var(--right-arrow-space);


	--color_cream: rgba(240, 224, 200, 1);
	--color_brown: rgba(54,30,23, 1);

	/*
	--url_scroll_bg: url("/scroll.jpeg");
	--url_background_leather: url('/background-leather.jpeg');
	--url_header_bg: url('/shelf.jpeg');
	--url_book: url("/book1.jpeg");
	*/

	--url_header_bg: url('https://bear-images.sfo2.cdn.digitaloceanspaces.com/reedybear/shelf.webp');
	--url_scroll_bg: url('https://bear-images.sfo2.cdn.digitaloceanspaces.com/reedybear/scroll.webp');
	--url_book: url('https://bear-images.sfo2.cdn.digitaloceanspaces.com/reedybear/book1.webp');
	--url_background_leather: url('https://bear-images.sfo2.cdn.digitaloceanspaces.com/reedybear/background-leather.webp');

}


* {
	box-sizing: border-box;
}

html {
	background: var(--url_background_leather);
	background-repeat: repeat;

}
body {
	overflow-x: hidden;
	margin:0;
	margin-left: auto;
	margin-right: auto;

	height: 100vh;
	height: calc(100vh + var(--header_height));
	width: var(--book_width);

	background: var(--url_background_leather);
	background-repeat: repeat;
	position:relative;
}


footer {
	top:0;
	right:0;
	position: absolute;
	height:var(--header_height);
	display:flex;
	flex-direction: column;
	justify-content:space-between;
	text-align: right;
	font-size: 0.8em;
}
footer > span#footer-directive > p {
	display:flex;
	flex-direction: column;
	color: var(--color_cream);
	justify-content:end;
	background: rgba(54,30,23, 0.5);
	border-radius: 4px;
	box-shadow: 0px 0px 3px 3px var(--color_brown);
}
footer > span#footer-directive > p > a {
	color:var(--color_cream);

	text-decoration: none;

}
footer > span#footer-directive > p > a:hover {
	text-decoration: underline;
}

footer > span {
	color: var(--color_cream);
}
footer > span > a {

	color: var(--color_cream);
}
footer > span > a:hover {
	text-decoration: none;
}

header {
	background: var(--url_header_bg);
	background-size:cover;
	height: var(--header_height);
	width: var(--book_width);

	color: white;
	padding: 16px;


	box-shadow: 0px -8px 5px 10px var(--color_brown)
}
header a {
	color:white;
}
header > a {
	display: inline-block;
	padding: 4px 12px;
	border-radius: 27% 12px;
	border: 1px solid rgba(171,155,130,1);
	text-decoration:none;

	background:var(--color_brown);
	color: var(--color_cream); 
}

header > a:hover {
	border-color: var(--color_brown);
	background-color: var(--color_cream);
	color: var(--color_brown);
}
header > a > h1 {
	margin-top: 8px;
	margin-bottom: 12px;
}

header > nav > p {

	display: inline-block;
	padding: 0px 8px 0px 8px;
	text-decoration:none;

	background: rgba(54,30,23, 0.9);
	border: 1px solid rgba(171,155,130,0.9);
	border-radius: 12px 15%;
}

header > nav > p > a {
	display: inline-block;
	text-decoration:none;
	padding: 6px 6px;

	border-left: 1px solid transparent;
	border-right: 1px solid transparent;

	margin-left: 8px;
	color: var(--color_cream);
}

header > nav > p > a:hover {
	border-color: var(--color_cream);
	background-color: var(--color_cream);
	color:var(--color_brown);
}

main {

	color: var(--color_brown);
	box-sizing: border-box;
	columns: 2;
	background: var(--url_book);
	background-size: contain;
	overflow-x: scroll;

	background-repeat: no-repeat;
	grid-gap: var(--right-pad);

	scroll-snap-type: x mandatory;


	width: var(--book_width);
	height: var(--book_height);

	padding-right: var(--right-pad);
	padding-left: var(--left-pad);
	padding-top: 5.5vh;
	padding-bottom: 2.5vh;


	box-shadow: 0px -4px 10px 10px var(--color_brown)

}

main a {

	color: var(--color_brown);
}

main .highlight:has(> pre) {
	max-height: calc(var(--book_height) - 9.5vh);
	overflow-y: scroll;
	overflow-x: scroll;
}

main > p {
	scroll-snap-align: start;
	scroll-margin-left: 55px;
	scroll-margin-left: calc( var(--book_width) * .0610 );

	/* width: 378px vs book_width of 805 */
	/* need ration so that I can set the scroll-margin-left so that scroll snap positions are correct */
}

.leftarrow, .rightarrow {
	position: absolute;
	top:0;
	margin-top: var(--header_height);
	color: var(--color_cream);
	height: var(--book_height);
	line-height: var(--book_height);
	cursor: pointer;
	text-align: center;

	font-size: var(--arrow_font_size);
	user-select: none;
	background: none;
	border: none;
}

.leftarrow:hover, .rightarrow:hover {
	background: rgba(54, 30, 23, 0.3);
}

.rightarrow {
	right: 0;
	margin-right: var(--arrow-margin);
	width: var(--right-arrow-space);
}
.leftarrow {
	left: 0;
	margin-left: var(--arrow-margin);
	width: var(--left-arrow-space);
}

.pagesleft {
	position: absolute;
	top:0;
	margin-top: var(--header_height);
	padding-top: 2vh;
	margin-left: calc(var(--left-pad) + 1.5vh);
	color: white;
	text-align: center;
	vertical-align:top;

	color: var(--color_brown);
	font-weight: bolder;
}

.pagesright {

	position: absolute;
	top:0;
	right:0;
	margin-top: var(--header_height);
	padding-top: 2vh;
	margin-right: calc(var(--arrow-margin) + var(--right-pad) + 3.5vh);
	color: white;
	text-align: center;
	vertical-align:top;

	color: var(--color_brown);
	font-weight: bolder;
}



@media (max-width: 600px) {
	:root {
		--header_height: 250px;
	}

	body {
		height: unset;
		overflow-x: visible;

	}
	main {
		overflow-x: visible;
		columns: 1;
		height: unset;
		background-repeat: repeat;
		background: var(--url_scroll_bg);
	}

	.leftarrow, .rightarrow, .pagesright, .pagesleft {
		display:none;
	}
}

Javsacript

Goes in Dashboard -> Settings -> Header and footer directives -> Head directive

<!-- MY BOOK THEME -->
<script>

const BookScroller = {
	min_width: 600,
	/* tracking the right-hand page number because it works with the maths */
	current_page: 2,
	book_el: null,
	rightarrow: null,
	leftarrow: null,
	pagesright: null,
	pagesleft: null,
	timeout: null,
};

BookScroller.get_num_pages =
	function(){
		const book_el = BookScroller.book_el;

		if (book_el.scrollLeftMax == 0){
			return 2;
		}

		const page_width = book_el.offsetWidth / 2;
		const page_count = Math.round(book_el.scrollWidth / page_width);
		//console.log(" ------ get_num_pages ------");
		//console.log("scrollWidth: "+book_el.scrollWidth);
		//console.log("offsetWidth: "+book_el.offsetWidth);
		//console.log("pageWidth: "+page_width);
		//console.log("numpages: "+page_count);

		return page_count;

	};

BookScroller.setup_dynamic_stuff = 
	function(){
		const page_count = BookScroller.get_num_pages();
		if (page_count <= 2){
			BookScroller.rightarrow.style.display = "none";
		}

		for (const n of document.querySelectorAll('p.book_page_extender')){
			n.parentNode.removeChild(n);
		}

		// ENSURE EVEN NUMBER OF PAGES
		const max_loops = 25;
		let num_loops = 0;
		while ((BookScroller.get_num_pages() % 2) != 0){
			const p = document.createElement('p');
			p.classList.add('book_page_extender');
			p.innerHTML = '&nbsp;';
			book_el.appendChild(p);
			// SANITY CHECK just to prevent infinte loops in case of bug
			if (num_loops++ >= max_loops)break;
		}

		const max_scroll_loops = 50;
		let scroll_loop_count = 0;
		while (BookScroller.current_page >2){
			BookScroller.scroll(-1);
			// Sanity check to prevent infinite loop in case of bug
			if (scroll_loop_count++ > max_scroll_loops)break;
		}
	};


BookScroller.init
	= function(){
		BookScroller.book_el = book_el = document.querySelector('body > main');

		BookScroller.rightarrow = rightarrow = document.createElement('button');
		rightarrow.classList.add("rightarrow");
		rightarrow.setAttribute("tabindex", 1);

		rightarrow.innerHTML = "&gt;";
		rightarrow.innerHTML = "&#10148"; // I couldn't find the left-hand equivalent
		rightarrow.innerHTML = "&#11166";

		document.querySelector('body').appendChild(rightarrow);


		BookScroller.leftarrow = leftarrow = document.createElement('button');
		leftarrow.classList.add("leftarrow");
		leftarrow.setAttribute("tabindex", 2);

		leftarrow.style.display = "none";

		leftarrow.innerHTML = "&lt;";
		leftarrow.innerHTML = "&#11164";

		document.querySelector('body').appendChild(leftarrow);


		BookScroller.pagesleft = pagesleft = document.createElement('div');
		pagesleft.classList.add('pagesleft');
		pagesleft.innerText = "1";
		document.querySelector('body').appendChild(pagesleft);


		BookScroller.pagesright = pagesright = document.createElement('div');
		pagesright.classList.add('pagesright');
		pagesright.innerText = "2";
		document.querySelector('body').appendChild(pagesright);


		rightarrow.addEventListener('click',
			BookScroller.scroll.bind(this,1)
		);
		leftarrow.addEventListener('click',
			BookScroller.scroll.bind(this,-1)
		);


		book_el.addEventListener("wheel", 
			function (e) {
				if (window.innerWidth <= 600){
					return;
				}

				if (e.target.tagName == 'pre' 
				   && e.target.parentNode.classList.contains('highlight')
				|| e.target.closest("div.highlight > pre") != null){
					return;
				}
				if (e.deltaY > 0){
					BookScroller.scroll(1);
				} else {
					BookScroller.scroll(-1);
				}
				e.preventDefault();
				e.stopPropagation();
			}
		);

		window.addEventListener("keydown",
			function (e) {
				if (window.innerWidth <= 600){
					return;
				}
				if (e.key == 'ArrowRight'){
					BookScroller.scroll(1);
					e.preventDefault();
					e.stopPropagation();
				} else if (e.key == 'ArrowLeft') {
					BookScroller.scroll(-1);
					e.preventDefault();
					e.stopPropagation();
				}
			}
		);


		BookScroller.setup_dynamic_stuff();
	}


document.addEventListener('DOMContentLoaded',
	function(){
		BookScroller.init();
	}
);

window.addEventListener('resize',
	function(){
		clearTimeout(BookScroller.timeout);
		setTimeout(BookScroller.setup_dynamic_stuff, 250);
	}
);


BookScroller.scroll = 
	/** direction is 1 or -1 for right or left */
	function(direction){

		//console.log(BookScroller);
		//console.log(BookScroller.get_num_pages());

		if (window.innerWidth <= BookScroller.min_width){
			// we're showing mobile view, no book view
			return;
		}
		if (direction > 0 && BookScroller.current_page >= BookScroller.get_num_pages()){
			// trying to scroll right, but there are no pages to scroll to
			return;
		}

		if (direction < 0 && BookScroller.current_page <= 2){
			// trying to scroll left, but we're at the first two pages already
			return;
		}

		BookScroller.current_page += (direction * 2);

		const book_el = BookScroller.book_el;

		// scroll distance is the book width - left pad - right pad + grid gap
			// grid gap & right pad are the same
			// these formulas are defined in the CSS
		let left_pad = book_el.offsetWidth * 0.035 + .025*window.innerHeight;
		let scroll_distance = book_el.offsetWidth - left_pad;
		scroll_pos = scroll_distance * ((BookScroller.current_page/2)-1);

		//book_el.scrollIntoView();
		book_el.scroll(scroll_pos, 0);

		//console.log("CurrentPage: "+BookScroller.current_page);
		if (BookScroller.current_page >= BookScroller.get_num_pages()){
			//console.log("hide right arrow!");
			BookScroller.rightarrow.style.display = "none";
		} else {
			BookScroller.rightarrow.style.display = "block";
		}
		if (BookScroller.current_page <= 2){
			BookScroller.leftarrow.style.display = "none";
		} else {
			BookScroller.leftarrow.style.display = "block";
		}

		pagesleft.innerText = BookScroller.current_page - 1;
		pagesright.innerText = BookScroller.current_page;

		//console.log(BookScroller);
		//console.log(BookScroller.get_num_pages());
	};

</script>

#bearblog