Browse Source

Initial commit after forking over from the starter project.

master
Zed A. Shaw 2 years ago
commit
557fdeaf38
  1. 279
      .eslintrc.js
  2. 114
      .gitignore
  3. 1
      LICENSE
  4. 10
      README.md
  5. 24
      lib/jasmine_examples/Player.js
  6. 9
      lib/jasmine_examples/Song.js
  7. 7569
      package-lock.json
  8. 57
      package.json
  9. 1
      public/build/bundle.css
  10. 9978
      public/build/bundle.js
  11. 1
      public/build/bundle.js.map
  12. BIN
      public/favicon.png
  13. 449
      public/global.css
  14. 1
      public/icons/feather-sprite.svg
  15. 18
      public/index.html
  16. 76
      rollup.config.js
  17. 117
      scripts/setupTypeScript.js
  18. 15
      spec/helpers/jasmine_examples/SpecHelper.js
  19. 60
      spec/jasmine_examples/PlayerSpec.js
  20. 11
      spec/support/jasmine.json
  21. 1
      src/About.svelte
  22. 59
      src/App.svelte
  23. 128
      src/Home.svelte
  24. 1
      src/NotFound.svelte
  25. 30
      src/Sidebar.svelte
  26. 42
      src/components/CodeBubble.svelte
  27. 31
      src/components/Darkmode.svelte
  28. 35
      src/components/Icon.svelte
  29. 102
      src/demos/Google.svelte
  30. 143
      src/demos/Instagram.svelte
  31. 24
      src/demos/Login.svelte
  32. 181
      src/demos/Pinterest.svelte
  33. 327
      src/demos/Twitter.svelte
  34. 155
      src/demos/XorAcademy.svelte
  35. 241
      src/demos/XorAcademyWatch.svelte
  36. 279
      src/demos/Youtube.svelte
  37. 81
      src/demos/index.svelte
  38. 10
      src/main.js
  39. 27
      src/routes.js
  40. 101
      src/thumbs/Google.svelte
  41. 114
      src/thumbs/Instagram.svelte
  42. 24
      src/thumbs/Login.svelte
  43. 126
      src/thumbs/Pinterest.svelte
  44. 130
      src/thumbs/Twitter.svelte
  45. 110
      src/thumbs/Youtube.svelte

279
.eslintrc.js

@ -0,0 +1,279 @@
module.exports = {
"env": {
"browser": true,
"es6": true,
"node": true,
},
"plugins": ["jest"],
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
"accessor-pairs": "error",
"array-bracket-newline": "off",
"array-bracket-spacing": [
"error",
"never"
],
"array-callback-return": "error",
"array-element-newline": "off",
"arrow-body-style": "off",
"arrow-parens": "off",
"arrow-spacing": [
"error",
{
"after": true,
"before": true
}
],
"block-scoped-var": "error",
"block-spacing": "error",
"brace-style": [
"error",
"1tbs"
],
"callback-return": "off",
"camelcase": "off",
"capitalized-comments": "off",
"class-methods-use-this": "error",
"comma-dangle": "off",
"comma-spacing": [
"error",
{
"after": true,
"before": false
}
],
"comma-style": [
"error",
"last"
],
"complexity": "error",
"computed-property-spacing": [
"error",
"never"
],
"consistent-return": "error",
"consistent-this": "error",
"curly": "off",
"default-case": "error",
"dot-location": "error",
"dot-notation": "off",
"eol-last": "error",
"eqeqeq": "off",
"func-call-spacing": "error",
"func-name-matching": "error",
"func-names": "error",
"func-style": [
"error",
"expression"
],
"function-paren-newline": "off",
"generator-star-spacing": "error",
"global-require": "off",
"guard-for-in": "error",
"handle-callback-err": "error",
"id-blacklist": "error",
"id-length": "off",
"id-match": "error",
"implicit-arrow-linebreak": [
"error",
"beside"
],
"indent": "off",
"indent-legacy": "off",
"init-declarations": "error",
"jsx-quotes": "error",
"key-spacing": "warn",
"keyword-spacing": "off",
"line-comment-position": "off",
"linebreak-style": [
"error",
"unix"
],
"lines-around-comment": "error",
"lines-around-directive": "error",
"lines-between-class-members": "error",
"max-classes-per-file": "off",
"max-depth": "error",
"max-len": "off",
"max-lines": "off",
"max-lines-per-function": "off",
"max-nested-callbacks": "error",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "error",
"multiline-comment-style": "off",
"new-cap": "error",
"new-parens": "error",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "off",
"no-alert": "error",
"no-array-constructor": "error",
"no-async-promise-executor": "error",
"no-await-in-loop": "error",
"no-bitwise": "error",
"no-buffer-constructor": "error",
"no-caller": "error",
"no-catch-shadow": "error",
"no-confusing-arrow": "error",
"no-continue": "error",
"no-div-regex": "error",
"no-duplicate-imports": "error",
"no-else-return": "off",
"no-empty-function": "error",
"no-eq-null": "off",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-label": "error",
"no-extra-parens": "error",
"no-floating-decimal": "error",
"no-implicit-coercion": "error",
"no-implicit-globals": "error",
"no-implied-eval": "error",
"no-inline-comments": "off",
"no-invalid-this": "error",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-loop-func": "error",
"no-magic-numbers": "off",
"no-misleading-character-class": "error",
"no-mixed-operators": "error",
"no-mixed-requires": "error",
"no-multi-assign": "error",
"no-multi-spaces": "off",
"no-multi-str": "error",
"no-multiple-empty-lines": "error",
"no-native-reassign": "error",
"no-negated-condition": "off",
"no-negated-in-lhs": "error",
"no-nested-ternary": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-object": "error",
"no-new-require": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-param-reassign": "error",
"no-path-concat": "error",
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
"no-process-env": "off",
"no-process-exit": "off",
"no-proto": "error",
"no-prototype-builtins": "error",
"no-restricted-globals": "error",
"no-restricted-imports": "error",
"no-restricted-modules": "error",
"no-restricted-properties": "error",
"no-restricted-syntax": "error",
"no-return-assign": "error",
"no-return-await": "error",
"no-script-url": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow": "off",
"no-shadow-restricted-names": "error",
"no-spaced-func": "error",
"no-sync": "warn",
"no-tabs": "off",
"no-template-curly-in-string": "error",
"no-ternary": "off",
"no-throw-literal": "error",
"no-trailing-spaces": "off",
"no-undef-init": "error",
"no-undefined": "off",
"no-underscore-dangle": "off",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
"no-unused-expressions": "error",
"no-use-before-define": ["error", {"functions": true, "classes": false}],
"no-useless-call": "error",
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-var": "error",
"no-void": "error",
"no-warning-comments": "off",
"no-whitespace-before-property": "error",
"no-with": "error",
"nonblock-statement-body-position": "error",
"object-curly-newline": "off",
"object-curly-spacing": "off",
"object-shorthand": "error",
"one-var": "off",
"one-var-declaration-per-line": "error",
"operator-assignment": "error",
"operator-linebreak": "error",
"padded-blocks": "off",
"padding-line-between-statements": "error",
"prefer-arrow-callback": "error",
"prefer-const": "off",
"prefer-destructuring": "off",
"prefer-numeric-literals": "error",
"prefer-object-spread": "error",
"prefer-promise-reject-errors": "error",
"prefer-reflect": "off",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"quote-props": "off",
"quotes": "off",
"radix": "error",
"require-atomic-updates": "warn",
"require-await": "off",
"require-jsdoc": "error",
"require-unicode-regexp": "error",
"rest-spread-spacing": "error",
"semi": "off",
"semi-spacing": "error",
"semi-style": [
"error",
"last"
],
"sort-imports": "off",
"sort-keys": "off",
"sort-vars": "error",
"space-before-blocks": "error",
"space-before-function-paren": "off",
"space-in-parens": [
"error",
"never"
],
"space-infix-ops": "off",
"space-unary-ops": "error",
"spaced-comment": [
"error",
"always"
],
"strict": "error",
"switch-colon-spacing": "error",
"symbol-description": "error",
"template-curly-spacing": [
"error",
"never"
],
"template-tag-spacing": "error",
"unicode-bom": [
"error",
"never"
],
"valid-jsdoc": "error",
"vars-on-top": "error",
"wrap-iife": "error",
"wrap-regex": "error",
"yield-star-spacing": "error",
"yoda": [
"error",
"never"
]
}
};

114
.gitignore

@ -0,0 +1,114 @@
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# FuseBox cache
.fusebox/
# ---> Vim
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
.DS_Store
/node_modules/
/src/node_modules/@sapper/
yarn-error.log
/cypress/screenshots/
.DS_Store
/node_modules/
/src/node_modules/@sapper/
yarn-error.log
/cypress/screenshots/
/__sapper__/
.sessions
dump.rdb
devices/
*.mp4
*.webm
*.jpg

1
LICENSE

@ -0,0 +1 @@
There is no license given at all for this code. It is fully copyright like any creative work you find online. Think of this as being like a photographer's photo or a painting in a museum. You can look at it and marvel at it's glorious beauty, but you can't take it home or use it without permission.

10
README.md

@ -0,0 +1,10 @@
Notes for starter-project:
* Keep it simple and just an SPA with svelte-spa
* Include everything needed to get started with a full stack. Database, knex, sqlite3, etc.
* Logins, passwords, and payments included ready to go.
* Email templates and sending built-in and ready.
I installed most of the stuff so I think what I have to do is start building a first thing, then add
the components I pull out to it as I go.

24
lib/jasmine_examples/Player.js

@ -0,0 +1,24 @@
function Player() {
}
Player.prototype.play = function(song) {
this.currentlyPlayingSong = song;
this.isPlaying = true;
};
Player.prototype.pause = function() {
this.isPlaying = false;
};
Player.prototype.resume = function() {
if (this.isPlaying) {
throw new Error("song is already playing");
}
this.isPlaying = true;
};
Player.prototype.makeFavorite = function() {
this.currentlyPlayingSong.persistFavoriteStatus(true);
};
module.exports = Player;

9
lib/jasmine_examples/Song.js

@ -0,0 +1,9 @@
function Song() {
}
Song.prototype.persistFavoriteStatus = function(value) {
// something complicated
throw new Error("not yet implemented");
};
module.exports = Song;

7569
package-lock.json

File diff suppressed because it is too large

57
package.json

@ -0,0 +1,57 @@
{
"name": "svelte-app",
"version": "1.0.0",
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public",
"test": "jasmine"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-node-resolve": "^10.0.0",
"axios": "^0.21.0",
"axios-cookiejar-support": "^1.0.1",
"chromedriver": "^87.0.0",
"faker": "^5.1.0",
"jasmine": "^3.6.3",
"maildev": "^1.1.0",
"node-notifier": "^8.0.0",
"pm2": "^4.5.0",
"pm2-logrotate": "^2.7.0",
"puppeteer": "^5.5.0",
"rollup": "^2.3.4",
"rollup-plugin-css-only": "^3.0.0",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"svelte": "^3.0.0"
},
"dependencies": {
"Validator": "^1.1.2",
"bcrypt": "^5.0.0",
"body-parser": "^1.19.0",
"bull": "^3.20.0",
"compression": "^1.7.4",
"cookie-session": "^1.4.0",
"dayjs": "^1.9.6",
"email-deep-validator": "^3.3.0",
"eslint": "^7.14.0",
"express-session": "^1.17.1",
"knex": "^0.21.12",
"knex-paginate": "^1.2.3",
"morgan": "^1.10.0",
"node-fetch": "^2.6.1",
"nodemailer": "^6.4.16",
"passport": "^0.4.1",
"passport-local": "^1.0.0",
"pg": "^8.5.1",
"pino": "^6.7.0",
"pino-http": "^5.3.0",
"pino-pretty": "^4.3.0",
"polka": "^0.5.2",
"sirv-cli": "^1.0.0",
"sqlite3": "^5.0.0",
"svelte-spa-router": "^3.0.5"
}
}

1
public/build/bundle.css

File diff suppressed because one or more lines are too long

9978
public/build/bundle.js

File diff suppressed because it is too large

1
public/build/bundle.js.map

File diff suppressed because one or more lines are too long

BIN
public/favicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

449
public/global.css

@ -0,0 +1,449 @@
/* MVP.css v1.6.2 - https://github.com/andybrewer/mvp */
:root {
--border-radius: 5px;
--box-shadow: 2px 2px 10px;
--color: #000;
--color-accent: #111;
--color-inactive: #999;
--color-inactive-secondary: #fff;
--color-bg: #fff;
--color-bg-secondary: #eee;
--color-secondary: #666;
--color-secondary-accent: #888;
--color-shadow: #ddd;
--color-shadow-secondary: #fff;
--color-bg-inverted: #555;
--color-text: #000;
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
--hover-brightness: 1.2;
--justify-important: center;
--justify-normal: left;
--line-height: 1.5;
--width-card: 285px;
--width-card-medium: 460px;
--width-card-wide: 800px;
--width-content: 1090px;
}
[data-theme="dark"] {
--color: #bbb;
--color-accent: #fff;
--color-bg: #333;
--color-bg-secondary: #555;
--color-shadow-secondary: #333;
--color-secondary: #ddd;
--color-secondary-accent: #ddd;
--color-shadow: #bbb;
--color-text: #fff;
--color-text-secondary: #aaa;
--color-inactive: #666;
--color-bg-inverted: #fff;
}
/* Layout */
article aside {
background: var(--color-secondary-accent);
border-left: 4px solid var(--color-secondary);
padding: 0.01rem 0.8rem;
}
body {
background: var(--color-bg);
color: var(--color-text);
font-family: var(--font-family);
line-height: var(--line-height);
margin: 0;
overflow-x: hidden;
padding: 1rem 0;
}
footer,
header,
main {
margin: 0 auto;
max-width: var(--width-content);
}
hr {
background-color: var(--color-bg-secondary);
border: none;
height: 1px;
margin: 4rem 0;
}
section {
display: flex;
flex-wrap: wrap;
justify-content: var(--justify-important);
}
section aside {
border: 1px solid var(--color-bg-secondary);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow) var(--color-shadow);
margin: 1rem;
padding: 1.25rem;
width: var(--width-card);
}
section aside:hover {
box-shadow: var(--box-shadow) var(--color-bg-secondary);
}
section aside img {
max-width: 100%;
}
[hidden] {
display: none;
}
/* Headers */
article header,
div header,
main header {
padding-top: 0;
}
header {
text-align: var(--justify-important);
}
header a b,
header a em,
header a i,
header a strong {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
header nav img {
margin: 1rem 0;
}
section header {
padding-top: 0;
width: 100%;
}
/* Nav */
nav {
align-items: center;
display: flex;
font-weight: bold;
justify-content: space-between;
}
nav ul {
list-style: none;
padding: 0;
}
nav ul li {
display: inline-block;
margin: 0 0.5rem;
position: relative;
text-align: left;
}
/* Nav Dropdown */
nav ul li:hover ul {
display: block;
}
nav ul li ul {
background: var(--color-bg);
border: 1px solid var(--color-bg-secondary);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow) var(--color-shadow);
display: none;
height: auto;
left: -2px;
padding: .5rem 1rem;
position: absolute;
top: 1.7rem;
white-space: nowrap;
width: auto;
}
nav ul li ul li,
nav ul li ul li a {
display: block;
}
/* Typography */
code,
samp {
background-color: var(--color-accent);
border-radius: var(--border-radius);
color: var(--color-text);
display: inline-block;
margin: 0 0.1rem;
padding: 0 0.5rem;
}
details {
margin: 1.3rem 0;
}
details summary {
font-weight: bold;
cursor: pointer;
}
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: var(--line-height);
}
mark {
padding: 0.1rem;
}
ol li,
ul li {
padding: 0.2rem 0;
}
p {
padding: 0;
}
pre {
margin: 1rem 0;
max-width: var(--width-card-wide);
padding: 1rem 0;
}
pre code,
pre samp {
display: block;
max-width: var(--width-card-wide);
padding: 0.5rem 2rem;
white-space: pre-wrap;
}
small {
color: var(--color-text-secondary);
}
sup {
background-color: var(--color-secondary);
border-radius: var(--border-radius);
color: var(--color-bg);
font-size: xx-small;
font-weight: bold;
margin: 0.2rem;
padding: 0.2rem 0.3rem;
position: relative;
top: -2px;
}
/* Links */
a {
--text-decoration: none;
color: var(--color-secondary);
display: inline-block;
font-weight: bold;
text-decoration: var(--text-decoration);
}
a:hover {
filter: brightness(var(--hover-brightness));
text-decoration: underline;
}
a b,
a em,
a i,
a strong,
button {
border-radius: var(--border-radius);
display: inline-block;
font-size: medium;
font-weight: bold;
line-height: var(--line-height);
padding: 1rem 2rem;
}
button {
font-family: var(--font-family);
}
button:hover {
cursor: pointer;
filter: brightness(var(--hover-brightness));
}
a b,
a strong,
button {
background-color: var(--color);
border: 2px solid var(--color);
color: var(--color-bg);
}
a em,
a i {
border: 2px solid var(--color);
border-radius: var(--border-radius);
color: var(--color);
display: inline-block;
padding: 1rem 2rem;
}
/* Images */
figure {
margin: 0;
padding: 0;
}
figure img {
max-width: 100%;
}
figure figcaption {
color: var(--color-text-secondary);
}
/* Forms */
button:disabled,
input:disabled {
background: var(--color-bg-secondary);
border-color: var(--color-bg-secondary);
color: var(--color-text-secondary);
cursor: not-allowed;
}
button[disabled]:hover {
filter: none;
}
form {
border: 1px solid var(--color-bg-secondary);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow) var(--color-shadow);
display: block;
max-width: var(--width-card-wide);
min-width: var(--width-card);
padding: 1.5rem;
text-align: var(--justify-normal);
}
form header {
margin: 1.5rem 0;
padding: 1.5rem 0;
}
input,
label,
select,
textarea {
display: block;
font-size: inherit;
max-width: var(--width-card-wide);
}
input[type="checkbox"],
input[type="radio"] {
display: inline-block;
}
input[type="checkbox"]+label,
input[type="radio"]+label {
display: inline-block;
font-weight: normal;
position: relative;
top: 1px;
}
input,
select,
textarea {
border: 1px solid var(--color-bg-secondary);
border-radius: var(--border-radius);
margin-bottom: 1rem;
padding: 0.4rem 0.8rem;
}
input[readonly],
textarea[readonly] {
background-color: var(--color-bg-secondary);
}
label {
font-weight: bold;
margin-bottom: 0.2rem;
}
/* Tables */
table {
border: 1px solid var(--color-bg-secondary);
border-radius: var(--border-radius);
border-spacing: 0;
display: inline-block;
max-width: 100%;
overflow-x: auto;
padding: 0;
white-space: nowrap;
}
table td,
table th,
table tr {
padding: 0.4rem 0.8rem;
text-align: var(--justify-important);
}
table thead {
background-color: var(--color);
border-collapse: collapse;
border-radius: var(--border-radius);
color: var(--color-bg);
margin: 0;
padding: 0;
}
table thead th:first-child {
border-top-left-radius: var(--border-radius);
}
table thead th:last-child {
border-top-right-radius: var(--border-radius);
}
table thead th:first-child,
table tr td:first-child {
text-align: var(--justify-normal);
}
table tr:nth-child(even) {
background-color: var(--color-accent);
}
/* Quotes */
blockquote {
display: block;
font-size: x-large;
line-height: var(--line-height);
margin: 1rem auto;
max-width: var(--width-card-medium);
padding: 1.5rem 1rem;
text-align: var(--justify-important);
}
blockquote footer {
color: var(--color-text-secondary);
display: block;
font-size: small;
line-height: var(--line-height);
padding: 1.5rem 0;
}

1
public/icons/feather-sprite.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 58 KiB

18
public/index.html

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>fsck CSS</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>

76
rollup.config.js

@ -0,0 +1,76 @@
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import css from 'rollup-plugin-css-only';
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
compilerOptions: {
// enable run-time checks when not in production
dev: !production
}
}),
// we'll extract any component CSS out into
// a separate file - better for performance
css({ output: 'bundle.css' }),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};

117
scripts/setupTypeScript.js

@ -0,0 +1,117 @@
// @ts-check
/** This script modifies the project to support TS code in .svelte files like:
<script lang="ts">
export let name: string;
</script>
As well as validating the code for CI.
*/
/** To work on this script:
rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
*/
const fs = require("fs")
const path = require("path")
const { argv } = require("process")
const projectRoot = argv[2] || path.join(__dirname, "..")
// Add deps to pkg.json
const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
"svelte-check": "^1.0.0",
"svelte-preprocess": "^4.0.0",
"@rollup/plugin-typescript": "^6.0.0",
"typescript": "^3.9.3",
"tslib": "^2.0.0",
"@tsconfig/svelte": "^1.0.0"
})
// Add script for checking
packageJSON.scripts = Object.assign(packageJSON.scripts, {
"validate": "svelte-check"
})
// Write the package JSON
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " "))
// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
fs.renameSync(beforeMainJSPath, afterMainTSPath)
// Switch the app.svelte file to use TS
const appSveltePath = path.join(projectRoot, "src", "App.svelte")
let appFile = fs.readFileSync(appSveltePath, "utf8")
appFile = appFile.replace("<script>", '<script lang="ts">')
appFile = appFile.replace("export let name;", 'export let name: string;')
fs.writeFileSync(appSveltePath, appFile)
// Edit rollup config
const rollupConfigPath = path.join(projectRoot, "rollup.config.js")
let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8")
// Edit imports
rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';
import typescript from '@rollup/plugin-typescript';`)
// Replace name of entry point
rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`)
// Add preprocessor
rollupConfig = rollupConfig.replace(
'compilerOptions:',
'preprocess: sveltePreprocess(),\n\t\t\tcompilerOptions:'
);
// Add TypeScript
rollupConfig = rollupConfig.replace(
'commonjs(),',
'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),'
);
fs.writeFileSync(rollupConfigPath, rollupConfig)
// Add TSConfig
const tsconfig = `{
"extends": "@tsconfig/svelte/tsconfig.json",
"include": ["src/**/*"],
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
}`
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
fs.writeFileSync(tsconfigPath, tsconfig)
// Delete this script, but not during testing
if (!argv[2]) {
// Remove the script
fs.unlinkSync(path.join(__filename))
// Check for Mac's DS_store file, and if it's the only one left remove it
const remainingFiles = fs.readdirSync(path.join(__dirname))
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
fs.unlinkSync(path.join(__dirname, '.DS_store'))
}
// Check if the scripts folder is empty
if (fs.readdirSync(path.join(__dirname)).length === 0) {
// Remove the scripts folder
fs.rmdirSync(path.join(__dirname))
}
}
// Adds the extension recommendation
fs.mkdirSync(path.join(projectRoot, ".vscode"))
fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
"recommendations": ["svelte.svelte-vscode"]
}
`)
console.log("Converted to TypeScript.")
if (fs.existsSync(path.join(projectRoot, "node_modules"))) {
console.log("\nYou will need to re-run your dependency manager to get started.")
}

15
spec/helpers/jasmine_examples/SpecHelper.js

@ -0,0 +1,15 @@
beforeEach(function () {
jasmine.addMatchers({
toBePlaying: function () {
return {
compare: function (actual, expected) {
var player = actual;
return {
pass: player.currentlyPlayingSong === expected && player.isPlaying
}
}
};
}
});
});

60
spec/jasmine_examples/PlayerSpec.js

@ -0,0 +1,60 @@
describe("Player", () => {
var Player = require('../../lib/jasmine_examples/Player');
var Song = require('../../lib/jasmine_examples/Song');
var player;
var song;
beforeEach(() => {
player = new Player();
song = new Song();
});
it("should be able to play a Song", () => {
player.play(song);
expect(player.currentlyPlayingSong).toEqual(song);
//demonstrates use of custom matcher
expect(player).toBePlaying(song);
});
describe("when song has been paused", () => {
beforeEach(() => {
player.play(song);
player.pause();
});
it("should indicate that the song is currently paused", () => {
expect(player.isPlaying).toBeFalsy();
// demonstrates use of 'not' with a custom matcher
expect(player).not.toBePlaying(song);
});
it("should be possible to resume", () => {
player.resume();
expect(player.isPlaying).toBeTruthy();
expect(player.currentlyPlayingSong).toEqual(song);
});
});
// demonstrates use of spies to intercept and test method calls
it("tells the current song if the user has made it a favorite", () => {
spyOn(song, 'persistFavoriteStatus');
player.play(song);
player.makeFavorite();
expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
});
//demonstrates use of expected exceptions
describe("#resume", () => {
it("should throw an exception if song is already playing", () => {
player.play(song);
expect(() => {
player.resume();
}).toThrowError("song is already playing");
});
});
});

11
spec/support/jasmine.json

@ -0,0 +1,11 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}

1
src/About.svelte

@ -0,0 +1 @@
<h1>About Page</h1>

59
src/App.svelte

@ -0,0 +1,59 @@
<script>
import Router from 'svelte-spa-router';
import routes from './routes';
import { link } from 'svelte-spa-router';
import Icon from './components/Icon.svelte';
import Darkmode from './components/Darkmode.svelte';
</script>
<svelte:head>
<title>LJSTHW Starter Template</title>
</svelte:head>
<style>
header {
}
main {
}
footer {
display: flex;
flex: flex-grow;
flex-direction: row;
}
footer nav {
flex: 1;
}
</style>
<header>
<nav>
<a href="/" use:link>
<Icon name="trash-2" size="64" />
</a>
<ul>
<li><a href="/demos" use:link>Demos</a></li>
<li><a href="/about" use:link>About</a></li>
</ul>
</nav>
</header>
<main>
<Router {routes } restoreScrollState={true} />
</main>
<hr>
<footer>
<nav>
<a href="/" use:link>
<p>Copyright (C) Big Corp.</p>
</a>
<ul>
<li><Icon name="twitter" size="32" /></li>
<li><Icon name="instagram" size="32" /></li>
<li><Darkmode /></li>
</ul>
</nav>
</footer>

128
src/Home.svelte

@ -0,0 +1,128 @@
<script>
import {link} from 'svelte-spa-router';
import Icon from './components/Icon.svelte';
</script>
<style>
hero {
display: block;
padding: 3rem;
background-color: var(--color-bg-secondary);
}
hero h1 {
font-size: 3rem;
}
aside a {
--text-decoration: underline;
}
rationale {
display: flex;
flex-direction: column;
font-size: 1.5em;
padding-left: 4rem;
padding-right: 4rem;
}
</style>
<hero>
<h1>fsck CSS</h1>
<p>An experiment in cleaning up CSS and HTML with modern tools like <b>flexbox</b> and <b>grids</b>.</p>
</hero>
<hr>
<section>
<aside>
<h3>Remove Classes</h3>
<Icon name="code" size="64" />
<p>Using a classless style like <a href="https://andybrewer.github.io/mvp/">MVP.css</a>, you
bring back CSS specificity to its original intent.</p>
</aside>
<aside>
<h3>Add Flexbox</h3>
<Icon name="box" size="64" />
<p>Using <a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/">flexbox</a> you can layout anything you want without tons of divs.</p>
</aside>
<aside>
<h3>Modify with Variables</h3>
<Icon name="sliders" size="64" />
<p>Using CSS variables you can replace many uses of classes and ids, avoiding most specificity
issues. Try the dark mode button below.
</p>
</aside>
</section>
<rationale>
<section>
<h1>Cleaner CSS</h1>
</section>
<p>This website is nothing more than a set of small demos for the idea of
using flexbox and grids to clean up and simplify CSS. Most everything else
about CSS stays. Filters, transforms, attributes, and everything that
applies to the visual display of the components of a design. What gets
removed is <b>div</b> heavy, class heavy, not-semantic-at-all layout
systems cluttering the real information available through simple HTML
tags.</p>
<p>In short, if you're writing this:</p>
<section>
<aside>
<pre>
&lt;div class="grid col-1">
&lt;div class="card">
...
&lt;/div>
&lt;/div>
</pre>
</aside>
</section>
<p>You could write this if you use flexbox, grids, and variables:</p>
<section>
<aside>
<pre>
&lt;card>
...
&lt;/card>
</pre>
</aside>
</section>
<hr>
<section>
<p>I demonstrate the idea with a series of copies of existing websites and other layout problems
people frequently encounter. If you have a suggested layout challenge for me, then tell me on Twitter <a href="https://twitter.com/lzsthw">@lzsthw</a> and I'll give it a shot.
</p>
<a href="/demos" use:link><button>View The Demos</button></a>
</section>
<hr>
<h1>What's wrong with classes?</h1>
<p>There's nothing wrong with classes in CSS. How they're used in recent CSS subverts the normal specificity rules making CSS unecessarily difficult. The original specificity rules put the base design of a component at the tag level, then a <b>class</b> can be used to modify and extend the design, and an <b>id</b> can make this even more specific. In today's use of <b>div.class</b> heavy markup you lose this ability to alter the design as needed where its used, and instead you have to resort to <b>!important</b> and specificity hacking to change how something looks, or add more <b>div</b> with more <b>class</b>.</p>
<p>The other problem with <b>div.class</b> is it's more difficult to debug problems with the layout. Because you're relying on specificity that can come from anywhere in the cascade it's much more difficult to find exactly <b>what</b> bit of CSS is overriding your local decisions. If you've ever had to open the inspector in your browser to troll through CSS locations looking for the culprit then that's why. If the base design decisions are at the tag level, then your local <b>class</b> and <b>id</b> specifics will work as expected. You can also replace many uses of class as design modifiers with scoped CSS variables and reduce the use of <b>div.class</b> even further.</p>
<p>Finally, using <b>div.class</b> heavy layout systems polutes your markup with structure that's difficult to maintain and change over time. When you focus on using <b>truly</b> semantic named tags and follow specificity as intended then your HTML is lighter and easier to understand because it isn't full of <b>div.grid.col-1.col-mx-auto</b> class rules that really have nothing to do with the actual meaning of that particular visual element. With <b>flexbox</b> and <b>CSS grids</b> you don't need any layout divs, and can keep the HTML clean and semantic. This also ends up being more flexible with less or the same effort as using a pre-made grid system full of divs.</p>
<h1>Should we ban div.class?</h1>
<p>Hell no. The current problem of <b>too much</b> class based CSS is because of trends in recent years calling for everyone to irrationally ban some practice. Remember when everyone decided tables were evil? So then we banned tables and <b>grids</b> completely? Then nobody could do a layout without crazy tricks and JavaScript? Nobody wants a repeat of the anti-tables "semantic markup" war, so please don't ban div.class.</p>
<p>All this website does is demonstrate that you don't need <b>so much div.class</b>. You can scrap a lot of what you're using now to do layout and go with clean easy to read HTML that has a nicer flat structure and is easy to maintain, then judiciously add in any extra things you find you need like <b>div.class</b>. Treat <b>div.class</b> like salt. Right now you're pouring a whole box of it on your HTML when a little bit makes it taste better.</p>
<h1>How did it get like this?</h1>
<p>Before flexbox and CSS grids there really was no choice but to use <b>div.class</b> to make grids for layout. Since everyone in the CSS design world irrationally banned tables as a layout mechanism your only choice was to invent another table but call it <b>div.grid.col-1</b>.</p>
<p>Today we don't have to use divs for layout anymore. It's entirely possible to use flexbox and CSS grids to solve many layout problems, and to do it without too many irrelevant alterations to the meaningful HTML. You no longer have to wrap an <b>img</b> with 5 <b>div</b> tags just to overlay it on a background. You can write the <b>img</b>, maybe a <b>figure</b> and then use CSS to do anything you want to it.</p>
</rationale>

1
src/NotFound.svelte

@ -0,0 +1 @@
<h1>Not found</h1>

30
src/Sidebar.svelte

@ -0,0 +1,30 @@
<style>
side {
background-color: var(--color-bg-secondary);
padding: 1em;
}
sidebar {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
height: 60vh;
}
content {
padding: 1em;
display: flex;
flex-direction: column;
flex: grow;
}
</style>
<sidebar>
<side>
<slot name="side"></slot>
</side>
<content>
<slot name="content"></slot>
</content>
</sidebar>

42
src/components/CodeBubble.svelte

@ -0,0 +1,42 @@
<script>
import Icon from '../components/Icon.svelte';
import { fade } from 'svelte/transition';
export let visible = false;
export let url = "";
const hide_code_button = () => {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
visible = false;
} else {
visible = true;
}
}
</script>
<svelte:window on:scroll={hide_code_button} />
<style>
code-bubble {
position: fixed;
bottom: 1em;
right: 1em;
}
code-bubble button {
background-color: var(--color-bg-secondary);
padding: 0.5em;
border-radius: 30%;
box-shadow: 4px 4px 4px var(--color-shadow);
}
</style>
{#if visible}
<code-bubble>
<a transition:fade href={ url }>
<button>
<Icon name="code" size="32" />
</button>
</a>
</code-bubble>
{/if}

31
src/components/Darkmode.svelte

@ -0,0 +1,31 @@
<script>
import Icon from './Icon.svelte';
import {onMount} from 'svelte';
export let theme = localStorage.getItem('theme') ? localStorage.getItem('theme') : 'light';
const set_theme = () => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}
const toggle = () => {
theme = theme == 'light' ? 'dark' : 'light';
set_theme();
}
onMount(() => {
set_theme()
});
</script>
{#if theme == 'dark'}
<span on:click={ () => toggle() }>
<Icon name="sunrise" size="32" />
</span>
{:else}
<span on:click={ () => toggle() }>
<Icon name="sunset" size="32"/>
</span>
{/if}

35
src/components/Icon.svelte

@ -0,0 +1,35 @@
<script>
export let size="24";
export let fill="none";
export let color="var(--color)";
export let light=false;
export let width="2";
export let linecap="round";
export let linejoin="round";
export let name;
export let inactive=false;
export let light_color = 'var(--color)';
</script>
<style lang="scss">
@import 'sass/_variables';
.inactive {
stroke: var(--color-inactive);
fill: var(--color-bg);
}
</style>
<span>
<svg class="icon-{name}" class:inactive="{inactive}"
width="{size}"
height="{size}"
fill="{fill}"
stroke="{light ? light_color : color}"
stroke-width="{width}"
stroke-linecap="{linecap}"
stroke-linejoin="{linejoin}"
>
<use xlink:href="/icons/feather-sprite.svg#{name}"/>
</svg>
</span>

102
src/demos/Google.svelte

@ -0,0 +1,102 @@
<script>
import { link } from 'svelte-spa-router';
import Icon from '../components/Icon.svelte';
</script>
<style>
content {
border: 1px solid #ddd;
display: flex;
flex-direction: column;
flex: flex-grow;
flex-basis: 100%;
grid-column: 1/3;
padding: 1rem;
}
header {
display: flex;
flex: flex-grow;
width: 100%;
flex-direction: row;
padding: unset;
}
nav {
flex: 1;
}
figure {
display: flex;
justify-content: center;
margin-top: 4rem;
}
search {