Phoenix 1.3 Rc0 and Bootstrap 4

Phoenix 1.3 rc0 has just been released and I want to create first versions of new apps for my home-page devekko.io and a political game and push it to a prgmr.com server using HashNukes Ansible.

This is part 1

Setup Elixir


brew install phoenix
brew install elixir

Phoenix 1.3 rc0

From the Elixir Forum the post on Phoenix 1.3 Released rc0 by Chris McCord


mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez

Trumps New Deal

I want to make a political game, Trumps New Deal, at first a super simple voting game. Thumbs up and thumbs down.

This generator is found in the install folder phoenix/installer/lib/mix/tasks/phx.new.ex


mix phx.new trumpsnewdealcom

So, in my Infra folder


➜ Infra mix phx.new trumpsnewdealcom
* creating trumpsnewdealcom/config/config.exs
* creating trumpsnewdealcom/config/dev.exs
* creating trumpsnewdealcom/config/prod.exs
* creating trumpsnewdealcom/config/prod.secret.exs
* creating trumpsnewdealcom/config/test.exs
* creating trumpsnewdealcom/lib/trumpsnewdealcom/application.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/channels/user_socket.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/views/error_helpers.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/views/error_view.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/endpoint.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/router.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/web.ex
* creating trumpsnewdealcom/mix.exs
* creating trumpsnewdealcom/README.md
* creating trumpsnewdealcom/test/support/channel_case.ex
* creating trumpsnewdealcom/test/support/conn_case.ex
* creating trumpsnewdealcom/test/test_helper.exs
* creating trumpsnewdealcom/test/web/views/error_view_test.exs
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/gettext.ex
* creating trumpsnewdealcom/priv/gettext/en/LC_MESSAGES/errors.po
* creating trumpsnewdealcom/priv/gettext/errors.pot
* creating trumpsnewdealcom/lib/trumpsnewdealcom/repo.ex
* creating trumpsnewdealcom/priv/repo/seeds.exs
* creating trumpsnewdealcom/test/support/data_case.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/controllers/page_controller.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/templates/layout/app.html.eex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/templates/page/index.html.eex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/views/layout_view.ex
* creating trumpsnewdealcom/lib/trumpsnewdealcom/web/views/page_view.ex
* creating trumpsnewdealcom/test/web/controllers/page_controller_test.exs
* creating trumpsnewdealcom/test/web/views/layout_view_test.exs
* creating trumpsnewdealcom/test/web/views/page_view_test.exs
* creating trumpsnewdealcom/.gitignore
* creating trumpsnewdealcom/assets/brunch-config.js
* creating trumpsnewdealcom/assets/css/app.css
* creating trumpsnewdealcom/assets/css/phoenix.css
* creating trumpsnewdealcom/assets/js/app.js
* creating trumpsnewdealcom/assets/js/socket.js
* creating trumpsnewdealcom/assets/package.json
* creating trumpsnewdealcom/assets/static/robots.txt
* creating trumpsnewdealcom/assets/static/images/phoenix.png
* creating trumpsnewdealcom/assets/static/favicon.ico

Fetch and install dependencies? [Yn] y
* running mix deps.get
* running mix deps.compile
* running cd assets && npm install && node node_modules/brunch/bin/brunch build

Database

we change directory


➜ Infra cd trumpsnewdealcom
➜ trumpsnewdealcom mix ecto.create
Compiling 12 files (.ex)
Generated trumpsnewdealcom app
The database for Trumpsnewdealcom.Repo has already been created
➜ trumpsnewdealcom iex -S mix phx.server
Erlang/OTP 19 [erts-8.2.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

[info] Running Trumpsnewdealcom.Web.Endpoint with Cowboy using http://0.0.0.0:4000
Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 09:51:00 - info: compiled 6 files into 2 files, copied 3 in 1.0 sec
iex(1)>

Live Reload

By default, Phoenix runs Live Reload and we can make changes to templates, code, styles, scripts, on the fly.

Change Homepage

Now, we change the homepage index template on this on the path

trumpsnewdealcom/lib/trumpsnewdealcom/web/templates/page/index.html.eex

New Style

Strip out the Phoenix default CSS from Bootstrap 3 from trumpsnewdealcom/assets/css/phoenix.css

Now we have a clean slate

Bootswatch 4

Now, I want to strip out Bootstrap 3 and replace with Bootswatch 4 and Bootstrap 4, we will customize from these.

Node Packages

We update package.json which manages our node_modules at trumpsnewdealcom/assets/package.json to include Bootswatch 4 from a branch on Github, its not on npm registry yet.

our /Users/devekko/Infra/trumpsnewdealcom/assets/package.json is now


{
"repository": {},
"license": "MIT",
"scripts": {
"deploy": "brunch build --production",
"watch": "brunch watch --stdin"
},
"dependencies": {
"phoenix": "file:../deps/phoenix",
"bootstrap": "^4.0.0-alpha.6",
"phoenix_html": "file:../deps/phoenix_html",
"bootswatch": "[email protected]:thomaspark/bootswatch.git#v4"
},
"devDependencies": {
"babel-brunch": "6.0.6",
"sass-brunch": "2.10.4",
"brunch": "2.10.7",
"copycat-brunch": "1.1.0",
"clean-css-brunch": "2.10.0",
"css-brunch": "2.10.0",
"uglify-js-brunch": "2.1.1"
}
}

the package.json of Bootswatch 4 confirms this trumpsnewdealcom/assets/node_modules/bootswatch/package.json


{
"_args": [
[
{
"raw": "[email protected]+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"scope": null,
"escapedName": "public",
"name": "public",
"rawSpec": "git+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"spec": "git+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"type": "hosted",
"hosted": {
"type": "github",
"ssh": "[email protected]:thomaspark/bootswatch.git#v4",
"sshUrl": "git+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"httpsUrl": "git+https://github.com/thomaspark/bootswatch.git#v4",
"gitUrl": "git://github.com/thomaspark/bootswatch.git#v4",
"shortcut": "github:thomaspark/bootswatch#v4",
"directUrl": "https://raw.githubusercontent.com/thomaspark/bootswatch/v4/package.json"
}
},
"/Users/devekko/Infra/trumpsnewdealcom/assets"
]
],
"_from": "git+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"_id": "[email protected]",
"_inCache": true,
"_location": "/bootswatch",
"_phantomChildren": {},
"_requested": {
"raw": "[email protected]+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"scope": null,
"escapedName": "public",
"name": "public",
"rawSpec": "git+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"spec": "git+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"type": "hosted",
"hosted": {
"type": "github",
"ssh": "[email protected]:thomaspark/bootswatch.git#v4",
"sshUrl": "git+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"httpsUrl": "git+https://github.com/thomaspark/bootswatch.git#v4",
"gitUrl": "git://github.com/thomaspark/bootswatch.git#v4",
"shortcut": "github:thomaspark/bootswatch#v4",
"directUrl": "https://raw.githubusercontent.com/thomaspark/bootswatch/v4/package.json"
}
},
"_requiredBy": [],
"_resolved": "git+ssh:[email protected][email protected]05",
"_shasum": "cc78a14f230879f4167b7fdca031a469fe02e1f3",
"_shrinkwrap": null,
"_spec": "[email protected]+ssh:[email protected]/thomaspark/bootswatch.git#v4",
"_where": "/Users/devekko/Infra/trumpsnewdealcom/assets",
"author": {
"name": "Thomas Park"
},
"bugs": {
"url": "https://github.com/thomaspark/bootswatch/issues"
},
"dependencies": {},
"description": "Bootswatch is a collection of themes for Bootstrap.",
"devDependencies": {
"autoprefixer": "^6.5.0",
"bower": "~1.2.8",
"grunt": "^1.0.1",
"grunt-contrib-clean": "^1.0.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-connect": "^1.0.2",
"grunt-contrib-sass": "^1.0.0",
"grunt-contrib-uglify": "^2.0.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-exec": "^1.0.1",
"postcss-cli": "^2.6.0",
"postcss-flexbugs-fixes": "^2.0.0"
},
"engines": {
"node": ">= 0.10.0"
},
"scripts": {
"postcss": "postcss --config assets/js/postcss.js --replace */bootstrap*.css"
},
"version": "4.0.0-alpha.6"
}

Copy Asset Files

app.scss

We now need to connect compile and connect to SCSS

we rename app.css to app.scss


/* This file is for your main application css. */
@import "css/bootswatch/_variables.scss";
@import "bootstrap";
@import "css/bootswatch/_bootswatch.scss";

Brunch config

Thanks to this gist I learned how to configure Brunch for Bootstrap 4 on Phoenix


exports.config = {
// See http://brunch.io/#documentation for docs.
files: {
javascripts: {
joinTo: "js/app.js"

},
stylesheets: {
joinTo: "css/app.css"
},
templates: {
joinTo: "js/app.js"
}
},

conventions: {
assets: /^(static)/
},

// Phoenix paths configuration
paths: {
// Dependencies and current project directories to watch
watched: ["static", "css", "js", "vendor"],
// Where to compile files to
public: "../priv/static"
},

// Configure your plugins
plugins: {
babel: {
// Do not use ES6 compiler in vendor code
ignore: [/vendor/]
},
sass: {
options: {
includePaths: ["node_modules/bootstrap/scss"], // Tell sass-brunch where to look for files to @import
precision: 8 // Minimum precision required by bootstrap-sass
}
},
},

modules: {
autoRequire: {
"js/app.js": ["js/app"]
}
},

npm: {
enabled: true,
globals: { // Bootstrap's JavaScript requires both '$' and 'jQuery' in global scope
$: 'jquery',
jQuery: 'jquery',
bootstrap: 'bootstrap' // Require Bootstrap's JavaScript globally
}
}
};

Bootswatch

I manually copy the Bootswatch folders across from node_modules to the assets folders.

It is possible to use npm postinstall cp -ar type solutions, but this becomes complex as I want to strip out some files like gulp build files and dist etc etc. There is no good way to do this imho and its seems to be confirmed by Cloudless Studio.

I also want to modify the Materia Bootswatch theme and I want to keep it simple. I struggled with this for a day and realized a simple copy and paste was best.

app.scss looks like


/* This file is for your main application css. */
@import "css/bootswatch/_variables.scss";
@import "bootstrap";
@import "css/bootswatch/_bootswatch.scss";

and my css assets folder, Bootstrap is in the node_modules folder and is configured via Brunch and compiled via SASS Brunch


tree css
css
├── app.scss
├── bootswatch
│   ├── LICENSE
│   ├── _bootswatch.scss
│   └── _variables.scss
└── phoenix.css

now our site has Bootstrap 4 / Bootswatch 4 styles

Kitchen Sink

To conclude this part of the project, I add a kitchen-sink page from Bootswatch so I can compare and contrast the fidelity of my build, check for missing JS and CSS etc etc.

I add trumpsnewdealcom/lib/trumpsnewdealcom/web/templates/page/kitchen.html.eex with HTML from Bootswatch

Page Controller updated

I add a new atom :kitchen for our kitchen sink


def kitchen(conn, _params) do
render conn, "kitchen.html"
end

and in the router trumpsnewdealcom/lib/trumpsnewdealcom/web/router.ex

I add a new route


scope "/", Trumpsnewdealcom.Web do
pipe_through :browser

get "/", PageController, :index
get "/kitchen", PageController, :kitchen

end

I copy the tag contents from https://bootswatch.com/4-alpha/materia/ and paste into kitchen.index.eex and we now we have a kitchen sink at http://0.0.0.0:4000/kitchen

Change Brand Primary

And of course, to make myself feel like a talented designer I change brand-primary

GOTCHA: and for some reason, I have to stop and restart phoenix server for the css to refresh, no doubt a setting in brunch. Will try to reproduce this and publish a repo.

stay tuned as I build out the game

ALSO, PLEASE DONT FLAME ME, I didn’t vote for Trump, but I do think there is a roll-back of the FDR New Deal, hence, Trumps New Deal