JSFiddle - React, Tailwind, and code Playground

by Gwyn Milcote

HTML

<!-- Panel, full width of page. -->
<div id='header-top'>
    <!-- Actual content restricted to 1000px. -->
    <div id='header-top-content'>
	
	    <!-- Username or HP/MP bars? Or if NLI, register link. -->
	    <div id='header-top-left'>
		    <span>Greetings, {username}!</span>
		</div>
		
		<!-- Logo in centre. -->
	    <div id='header-top-logo'>logo</div>
		
		<!-- Mail, currencies, logout button. Or if NLI, login form. -->
	    <div id='header-top-right'>
		    <span class='icon' id='header-span-mail'>1</span>
			<span class='separator'></span>
		    <span class='icon' id='header-span-coins'>86856</span>
			<span class='separator'></span>
		    <span class='icon' id='header-span-gems'>83</span>
			<span class='separator'></span>
			<span class='icon' id='header-span-logout'></span>
		</div>
		
	</div>
</div>

<!-- Below top panel, row of navigation icons. -->
<div id='header-navbar'>
	<ul id='navbar-content'></ul>
</div>

<!-- Begin main page, strictly inside container. -->
<div id='page-container'>

    <!-- Quest/tutorial dialogue panel might be here. -->
	<div id='quest-section'>[tutorial panel]</div>

    <!-- Page content varies... -->
	
	<!-- Large empty space where pet coat appears. -->
	<div id='pp-coat-container'>
	    
        <div class='mythic-coat coat-adult'></div>
        
	</div>
	
	<!-- Pet's name, title, and arrows to next/last pets. -->
	<div id='pp-banner'>
	    <div class='pp-banner-arrow'> < </div>
	    <div>
	        <div id='pp-banner-name'>Pet Name Goes Here</div>
	        <div id='pp-banner-title'>Pet Suffix Goes Here</div>
		</div>
	    <div class='pp-banner-arrow'> > </div>
	</div>
	
	<!-- Pet profile has 3 columns. -->
	<div id='pp-container'>
	
	    <div class='pp-column-side'>
		
		    <!-- Status -->
		    <div class='pp-section'>
			
			    <div class='pp-section-top'>
			        <div class='pp-section-question hover-outer'>?
					    <div class='hover-inner hover-inner-question'>How is your Mythic feeling right now? Be sure...

CSS

@import url('https://fonts.googleapis.com/css?family=Alegreya');

div, img, canvas, button, ul, li, p, a, table, tr, td {
    box-sizing: border-box;
}

body, html {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    font-family: 'Alegreya', sans-serif;
    color: rgba(0, 0, 0, 0.8);
}

body {
    background-image: url("https://2.bp.blogspot.com/_1PJ0u0wnGBA/TMF46D3ZEtI/AAAAAAAADjA/yugNSRhtDTU/s1600/Storm+Clouds+05.jpg");
    background-size: cover;
    background-attachment: fixed;
}

a {
    text-decoration: none;
}




/* Top of every page. Contains logo, user status. */
/* Below that, row of nav icons with dropdown menus. */
#header {
    
}

/* Full width of page. */
#header-top {
    width: 100%;
	height: 42px;
	background-image: url("header-top.png");
	background-color: #eee;
	border-bottom: 1px solid rgba(0, 0, 0, 0.8);
}

#header-top-content {
    width: 980px;
	margin: auto;
	position: relative;
}

#header-top-content span {
	display: inline-block;
	text-shadow: 0px 1px 1px #fff;
}

#header-top-left, #header-top-right {
    width: 340px;
	height: 40px;
	position: absolute;
	top: 0;
	border: 1px dotted red;
	display: flex;
	flex-flow: row;
	align-items: center;
}

#header-top-left {
	left: 0;
}
#header-top-right {
	right: 0;
	justify-content: right;
	text-align: right;
}


#header-top-logo {
    width: 300px;
	height: 50px;
	position: absolute;
	top: 0;
	left: 340px;
	/* Navbar is 2, so this must be above, partly covering the top */
	z-index: 3;
	background-color: rgba(255, 255, 255, 0.9);
	box-shadow: 0 0 10px 10px rgba(0, 0, 0, 0.4);
	background-image: url("header-logo.png");
}

/* Icons all on same sheet. Change background-position. */
#header-top-content span.separator {
    height: 20px;
	width: 2px;
	margin: 0px 10px;
    background-image: url("header-icons.png");
	background-position: 0 0;
	background-color: rgba(0, 0, 0, 0.8);
}

#header-top-content span.icon {
	height: 20px;
    background-image:...

JavaScript

// General utility funcs, include in main JS file.

function getSkillName(id){
    let skillNames = [
	    "Botany","Culinary","Geology","Metals",
		"Craft","Building","Science!","Textiles",
		"Combat","Toughness","Tracking","Agility",
		"Healing","Magic","Animals","Aquatics"
	];
    if(id < 1 || id > skillNames.length){
	    throw "getSkillName(): invalid ID, '" + id + "'.";
	}
	return skillNames[id - 1];
}

// Search an array of objects, by one property.
function findById(array,id){
    return array[id];
}
function findByProperty(array,prop,value){
    return array[id];
}
function sortByProperty(array,prop){
    return array;
}


// When click + or - icon in a box's top right corner,
// toggle the section's content on or off.
$(".pp-section-toggle").on("click", function(){
    let section = $(this).data("section"),
	    span = $("#pp-toggle-" + section);
	if(span.css("display") == "none"){
	    span.show();
	}else{
	    span.hide();
	}
});

// Store a bunch of pet vars... for easy reference/calcs/updating?
// Howrse does it, so it can't be a terrible idea.
let mythicInfo = {
    id: 7832,
    name: "Caristo",
	sex: 2,
	breed: 6,
	stage: 5,
	age: 127,
	energy: 89,
	health: 68,
	mood: 99,
	belly: 56,
	skills: [
       25,19,27,72, 
       18,10,73,16, 
       9,0,17,128, 
       47,297,238,70
   ]
};

class MythicProfile {

    constructor(data){
	    this.pet = data;
		
		// These should be available immediately.
		this.customisation = {
		    background: {id: 6, name: "Lavender Field"},
		    scenery: [
			    {
				    id: 1, layer: 3, vis: 1,
					name: "[Scenery] Fireflies"
				},{
				    id: 2, layer: 1, vis: 0,
					name: "[Scenery] Mist"
				},{
				    id: 3, layer: 2, vis: 1,
					name: "[Scenery] Tall Grass"
				},{
				    id: 4, layer: 6, vis: 1,
					name: "[Scenery] Butterflies"
				},{
				    id: 5, layer: 4, vis: 0,
					name: "[Scenery] Cherry Blossom"
				},{
				    id: 6, layer: 5, vis: 1,
					name: "[Scenery] Ember Rain"
				},{
				   ...