summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/_vendor/sassy-lists/stylesheets/functions/_contain.scss31
-rw-r--r--src/_vendor/sassy-lists/stylesheets/functions/_purge.scss38
-rw-r--r--src/_vendor/sassy-lists/stylesheets/functions/_remove.scss31
-rw-r--r--src/_vendor/sassy-lists/stylesheets/functions/_replace.scss46
-rw-r--r--src/_vendor/sassy-lists/stylesheets/functions/_to-list.scss27
-rw-r--r--src/_vendor/sassy-lists/stylesheets/helpers/_missing-dependencies.scss25
-rw-r--r--src/_vendor/sassy-lists/stylesheets/helpers/_true.scss13
-rw-r--r--src/components/app/App.jsx86
-rw-r--r--src/components/app/actionTypes.js22
-rw-r--r--src/components/app/actions.js37
-rw-r--r--src/components/app/reducers.js40
-rw-r--r--src/components/create-or-join/CreateOrJoin.jsx45
-rw-r--r--src/components/farm/Board.jsx1446
-rw-r--r--src/components/farm/MessagePanel.jsx50
-rw-r--r--src/components/farm/PlayerIcon.jsx30
-rw-r--r--src/components/farm/SpaceNode.jsx69
-rw-r--r--src/components/farm/actionTypes.js36
-rw-r--r--src/components/farm/actions.js115
-rw-r--r--src/components/farm/interface.js177
-rw-r--r--src/components/farm/reducers.js182
-rw-r--r--src/components/join-game/JoinGame.jsx97
-rw-r--r--src/components/new-game/NewGame.jsx104
-rw-r--r--src/components/start/Start.jsx94
-rw-r--r--src/components/start/actionTypes.js20
-rw-r--r--src/components/start/actions.js31
-rw-r--r--src/components/start/reducers.js37
-rw-r--r--src/components/tractor/Tractor.jsx40
-rw-r--r--src/components/welcome/Welcome.jsx46
-rw-r--r--src/components/widgets.jsx69
-rw-r--r--src/constants.js34
-rw-r--r--src/foundation/_global.scss244
-rw-r--r--src/foundation/_settings.scss896
-rw-r--r--src/foundation/components/_accordion-menu.scss174
-rw-r--r--src/foundation/components/_accordion.scss164
-rw-r--r--src/foundation/components/_badge.scss63
-rw-r--r--src/foundation/components/_breadcrumbs.scss119
-rw-r--r--src/foundation/components/_button-group.scss299
-rw-r--r--src/foundation/components/_button.scss428
-rw-r--r--src/foundation/components/_callout.scss108
-rw-r--r--src/foundation/components/_card.scss129
-rw-r--r--src/foundation/components/_close-button.scss127
-rw-r--r--src/foundation/components/_drilldown.scss140
-rw-r--r--src/foundation/components/_dropdown-menu.scss279
-rw-r--r--src/foundation/components/_dropdown.scss82
-rw-r--r--src/foundation/components/_flex-video.scss1
-rw-r--r--src/foundation/components/_flex.scss119
-rw-r--r--src/foundation/components/_float.scss27
-rw-r--r--src/foundation/components/_label.scss64
-rw-r--r--src/foundation/components/_media-object.scss114
-rw-r--r--src/foundation/components/_menu-icon.scss9
-rw-r--r--src/foundation/components/_menu.scss495
-rw-r--r--src/foundation/components/_off-canvas.scss511
-rw-r--r--src/foundation/components/_orbit.scss197
-rw-r--r--src/foundation/components/_pagination.scss201
-rw-r--r--src/foundation/components/_progress-bar.scss63
-rw-r--r--src/foundation/components/_responsive-embed.scss57
-rw-r--r--src/foundation/components/_reveal.scss185
-rwxr-xr-xsrc/foundation/components/_slider.scss137
-rw-r--r--src/foundation/components/_sticky.scss39
-rw-r--r--src/foundation/components/_switch.scss261
-rw-r--r--src/foundation/components/_table.scss328
-rw-r--r--src/foundation/components/_tabs.scss193
-rw-r--r--src/foundation/components/_thumbnail.scss67
-rw-r--r--src/foundation/components/_title-bar.scss84
-rw-r--r--src/foundation/components/_tooltip.scss160
-rw-r--r--src/foundation/components/_top-bar.scss175
-rw-r--r--src/foundation/components/_visibility.scss135
-rw-r--r--src/foundation/forms/_checkbox.scss41
-rw-r--r--src/foundation/forms/_error.scss89
-rw-r--r--src/foundation/forms/_fieldset.scss53
-rw-r--r--src/foundation/forms/_forms.scss34
-rw-r--r--src/foundation/forms/_help-text.scss30
-rw-r--r--src/foundation/forms/_input-group.scss142
-rw-r--r--src/foundation/forms/_label.scss50
-rw-r--r--src/foundation/forms/_meter.scss116
-rw-r--r--src/foundation/forms/_progress.scss94
-rw-r--r--src/foundation/forms/_range.scss149
-rw-r--r--src/foundation/forms/_select.scss90
-rw-r--r--src/foundation/forms/_text.scss179
-rw-r--r--src/foundation/foundation.scss155
-rw-r--r--src/foundation/grid/_classes.scss189
-rw-r--r--src/foundation/grid/_column.scss78
-rw-r--r--src/foundation/grid/_flex-grid.scss260
-rw-r--r--src/foundation/grid/_grid.scss48
-rw-r--r--src/foundation/grid/_gutter.scss67
-rw-r--r--src/foundation/grid/_layout.scss76
-rw-r--r--src/foundation/grid/_position.scss100
-rw-r--r--src/foundation/grid/_row.scss99
-rw-r--r--src/foundation/grid/_size.scss24
-rw-r--r--src/foundation/prototype/_arrow.scss36
-rw-r--r--src/foundation/prototype/_border-box.scss35
-rw-r--r--src/foundation/prototype/_border-none.scss35
-rw-r--r--src/foundation/prototype/_bordered.scss54
-rw-r--r--src/foundation/prototype/_box.scss23
-rw-r--r--src/foundation/prototype/_display.scss50
-rw-r--r--src/foundation/prototype/_font-styling.scss95
-rw-r--r--src/foundation/prototype/_list-style-type.scss95
-rw-r--r--src/foundation/prototype/_overflow.scss72
-rw-r--r--src/foundation/prototype/_position.scss114
-rw-r--r--src/foundation/prototype/_prototype.scss87
-rw-r--r--src/foundation/prototype/_relation.scss157
-rw-r--r--src/foundation/prototype/_rotate.scss31
-rw-r--r--src/foundation/prototype/_rounded.scss61
-rw-r--r--src/foundation/prototype/_separator.scss96
-rw-r--r--src/foundation/prototype/_shadow.scss43
-rw-r--r--src/foundation/prototype/_sizing.scss73
-rw-r--r--src/foundation/prototype/_spacing.scss179
-rw-r--r--src/foundation/prototype/_text-decoration.scss48
-rw-r--r--src/foundation/prototype/_text-transformation.scss48
-rw-r--r--src/foundation/prototype/_text-utilities.scss88
-rw-r--r--src/foundation/settings/_settings.scss895
-rw-r--r--src/foundation/typography/_alignment.scss22
-rw-r--r--src/foundation/typography/_base.scss474
-rw-r--r--src/foundation/typography/_helpers.scss180
-rw-r--r--src/foundation/typography/_print.scss96
-rw-r--r--src/foundation/typography/_typography.scss26
-rw-r--r--src/foundation/util/_breakpoint.scss435
-rw-r--r--src/foundation/util/_color.scss139
-rw-r--r--src/foundation/util/_direction.scss31
-rw-r--r--src/foundation/util/_flex.scss90
-rw-r--r--src/foundation/util/_math.scss147
-rw-r--r--src/foundation/util/_mixins.scss373
-rw-r--r--src/foundation/util/_selector.scss41
-rw-r--r--src/foundation/util/_typography.scss26
-rw-r--r--src/foundation/util/_unit.scss152
-rw-r--r--src/foundation/util/_util.scss14
-rw-r--r--src/foundation/util/_value.scss200
-rw-r--r--src/foundation/vendor/normalize.scss281
-rw-r--r--src/foundation/xy-grid/_cell.scss272
-rw-r--r--src/foundation/xy-grid/_classes.scss493
-rw-r--r--src/foundation/xy-grid/_collapse.scss75
-rw-r--r--src/foundation/xy-grid/_frame.scss86
-rw-r--r--src/foundation/xy-grid/_grid.scss37
-rw-r--r--src/foundation/xy-grid/_gutters.scss45
-rw-r--r--src/foundation/xy-grid/_layout.scss36
-rw-r--r--src/foundation/xy-grid/_position.scss55
-rw-r--r--src/foundation/xy-grid/_xy-grid.scss51
-rw-r--r--src/main.jsx100
-rw-r--r--src/rootReducers.js24
-rw-r--r--src/server/farm.scm1678
-rw-r--r--src/style.scss734
-rw-r--r--src/websocket.js92
142 files changed, 21075 insertions, 0 deletions
diff --git a/src/_vendor/sassy-lists/stylesheets/functions/_contain.scss b/src/_vendor/sassy-lists/stylesheets/functions/_contain.scss
new file mode 100644
index 0000000..87d160b
--- /dev/null
+++ b/src/_vendor/sassy-lists/stylesheets/functions/_contain.scss
@@ -0,0 +1,31 @@
+///
+/// Returns whether `$list` contains `$value`.
+///
+/// @ignore Documentation: http://at-import.github.io/SassyLists/documentation/#function-sl-contain
+///
+/// @param {List} $list - list to check
+/// @param {*} $value - value to look for
+///
+/// @example
+/// sl-contain(a b c, a)
+/// // true
+///
+/// @example
+/// sl-contain(a b c, z)
+/// // false
+///
+/// @return {Bool}
+///
+
+@function sl-contain($list, $value) {
+ @return not not index($list, $value);
+}
+
+///
+/// @requires sl-contain
+/// @alias sl-contain
+///
+
+@function sl-include($list, $value) {
+ @return sl-contain($list, $value);
+}
diff --git a/src/_vendor/sassy-lists/stylesheets/functions/_purge.scss b/src/_vendor/sassy-lists/stylesheets/functions/_purge.scss
new file mode 100644
index 0000000..63102bf
--- /dev/null
+++ b/src/_vendor/sassy-lists/stylesheets/functions/_purge.scss
@@ -0,0 +1,38 @@
+/// Removes all false and null values from `$list`.
+///
+/// @ignore Documentation: http://at-import.github.io/SassyLists/documentation/#function-sl-purge
+///
+/// @requires sl-is-true
+/// @requires sl-to-list
+///
+/// @param {List} $list - list to purge
+///
+/// @example
+/// sl-purge(null a false b)
+/// // a b
+///
+/// @return {List}
+///
+
+@function sl-purge($list) {
+ $_: sl-missing-dependencies('sl-is-true', 'sl-to-list');
+
+ $result: ();
+
+ @each $item in $list {
+ @if sl-is-true($item) {
+ $result: append($result, $item, list-separator($list));
+ }
+ }
+
+ @return sl-to-list($result);
+}
+
+///
+/// @requires sl-purge
+/// @alias sl-purge
+///
+
+@function sl-clean($list) {
+ @return sl-purge($list);
+}
diff --git a/src/_vendor/sassy-lists/stylesheets/functions/_remove.scss b/src/_vendor/sassy-lists/stylesheets/functions/_remove.scss
new file mode 100644
index 0000000..0282744
--- /dev/null
+++ b/src/_vendor/sassy-lists/stylesheets/functions/_remove.scss
@@ -0,0 +1,31 @@
+///
+/// Removes value(s) `$value` from `$list`.
+///
+/// @ignore Documentation: http://at-import.github.io/SassyLists/documentation/#function-sl-remove
+///
+/// @requires sl-replace
+///
+/// @param {List} $list - list to update
+/// @param {*} $value - value to remove
+///
+/// @example
+/// sl-remove(a b c, a)
+/// // b c
+///
+/// @return {List}
+///
+
+@function sl-remove($list, $value) {
+ $_: sl-missing-dependencies('sl-replace');
+
+ @return sl-replace($list, $value, null);
+}
+
+///
+/// @requires sl-remove
+/// @alias sl-remove
+///
+
+@function sl-without($list, $value) {
+ @return sl-remove($list, $value);
+}
diff --git a/src/_vendor/sassy-lists/stylesheets/functions/_replace.scss b/src/_vendor/sassy-lists/stylesheets/functions/_replace.scss
new file mode 100644
index 0000000..8e70ad5
--- /dev/null
+++ b/src/_vendor/sassy-lists/stylesheets/functions/_replace.scss
@@ -0,0 +1,46 @@
+///
+/// Replaces `$old` by `$new` in `$list`.
+///
+/// @ignore Documentation: http://at-import.github.io/SassyLists/documentation/#function-sl-replace
+///
+/// @requires sl-is-true
+/// @requires sl-purge
+/// @requires sl-to-list
+///
+/// @param {List} $list - list to update
+/// @param {*} $old - value to replace
+/// @param {*} $value - new value for $old
+///
+/// @example
+/// sl-replace(a b c, b, z)
+/// // a z c
+///
+/// @example
+/// sl-replace(a b c, y, z)
+/// // a b c
+///
+/// @return {List}
+///
+
+@function sl-replace($list, $old, $value) {
+ $_: sl-missing-dependencies('sl-is-true', 'sl-purge', 'sl-to-list');
+
+ $running: true;
+
+ @while $running {
+ $index: index($list, $old);
+
+ @if not $index {
+ $running: false;
+ }
+
+ @else {
+ $list: set-nth($list, $index, $value);
+ }
+
+ }
+
+ $list: if(sl-is-true($value), $list, sl-purge($list));
+
+ @return sl-to-list($list);
+}
diff --git a/src/_vendor/sassy-lists/stylesheets/functions/_to-list.scss b/src/_vendor/sassy-lists/stylesheets/functions/_to-list.scss
new file mode 100644
index 0000000..eb8df21
--- /dev/null
+++ b/src/_vendor/sassy-lists/stylesheets/functions/_to-list.scss
@@ -0,0 +1,27 @@
+///
+/// Casts `$value` into a list.
+///
+/// @ignore Documentation: http://at-import.github.io/SassyLists/documentation/#function-sl-to-list
+///
+/// @param {*} $value - value to cast to list
+/// @param {String} $separator [space] - separator to use
+///
+/// @example
+/// sl-to-list(a b c, comma)
+/// // a, b, c
+///
+/// @return {List}
+///
+
+@function sl-to-list($value, $separator: list-separator($value)) {
+ @return join((), $value, $separator);
+}
+
+///
+/// @requires sl-to-list
+/// @alias sl-to-list
+///
+
+@function sl-listify($value) {
+ @return sl-to-list($value);
+}
diff --git a/src/_vendor/sassy-lists/stylesheets/helpers/_missing-dependencies.scss b/src/_vendor/sassy-lists/stylesheets/helpers/_missing-dependencies.scss
new file mode 100644
index 0000000..c4730b1
--- /dev/null
+++ b/src/_vendor/sassy-lists/stylesheets/helpers/_missing-dependencies.scss
@@ -0,0 +1,25 @@
+///
+/// Checks whether `$functions` exist in global scope.
+///
+/// @access private
+///
+/// @param {ArgList} $functions - list of functions to check for
+///
+/// @return {Bool} Whether or not there are missing dependencies
+///
+
+@function sl-missing-dependencies($functions...) {
+ $missing-dependencies: ();
+
+ @each $function in $functions {
+ @if not function-exists($function) {
+ $missing-dependencies: append($missing-dependencies, $function, comma);
+ }
+ }
+
+ @if length($missing-dependencies) > 0 {
+ @error 'Unmet dependencies! The following functions are required: #{$missing-dependencies}.';
+ }
+
+ @return length($missing-dependencies) > 0;
+}
diff --git a/src/_vendor/sassy-lists/stylesheets/helpers/_true.scss b/src/_vendor/sassy-lists/stylesheets/helpers/_true.scss
new file mode 100644
index 0000000..277652e
--- /dev/null
+++ b/src/_vendor/sassy-lists/stylesheets/helpers/_true.scss
@@ -0,0 +1,13 @@
+///
+/// Returns truthiness of `$value`.
+///
+/// @access private
+///
+/// @param {*} $value - value to check
+///
+/// @return {Bool}
+///
+
+@function sl-is-true($value) {
+ @return if($value == null, false, $value and $value != null and $value != '' and $value != ());
+}
diff --git a/src/components/app/App.jsx b/src/components/app/App.jsx
new file mode 100644
index 0000000..9fae531
--- /dev/null
+++ b/src/components/app/App.jsx
@@ -0,0 +1,86 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React, { Fragment } from 'react'
+import { connect } from 'react-redux'
+
+import Board from '../farm/Board.jsx'
+import MessagePanel from '../farm/MessagePanel.jsx'
+import CreateOrJoin from '../create-or-join/CreateOrJoin.jsx'
+import NewGame from '../new-game/NewGame.jsx'
+import JoinGame from '../join-game/JoinGame.jsx'
+import Welcome from '../welcome/Welcome.jsx'
+import Tractor from '../tractor/Tractor.jsx'
+
+import { SCREENS, messagePanelId } from '../../constants.js'
+import { play } from './actions.js'
+
+class Chrome extends React.Component {
+ render() {
+ return (
+ <div className='flex-fullcenter'>
+ <div className='background-heading'><h1>Alpha Centauri Farming</h1></div>
+ {this.props.children}
+ <Tractor spikes={this.props.spikes} className={this.props.tractorClass} />
+ </div>
+ );
+ }
+}
+
+class App extends React.Component {
+ render() {
+ let view;
+ switch (this.props.screen) {
+ case SCREENS.intro:
+ view = (<Chrome spikes={true} tractorClass='intro'><Welcome /></Chrome>);
+ break;
+ case SCREENS.start:
+ view = (<Chrome><CreateOrJoin /></Chrome>);
+ break;
+ case SCREENS.newGame:
+ view = (<Chrome>
+ <div className='view-container'>
+ <NewGame colors={['green', 'red', 'blue', 'yellow', 'black']}
+ button={'Start'}
+ title={'New Game'}
+ type={'new-game'}
+ showGameName={true} />
+ </div>
+ </Chrome>);
+ break;
+ case SCREENS.joinGame:
+ view = (<Chrome><div className='view-container'><JoinGame /></div></Chrome>);
+ break;
+ case SCREENS.play:
+ view = (<Board />);
+ break;
+ }
+ return (
+ <Fragment>
+ {view}
+ <div id={messagePanelId}><MessagePanel /></div>
+ </Fragment>
+ );
+ }
+}
+
+export default connect(
+ state => state.app,
+ null
+)(App);
+
diff --git a/src/components/app/actionTypes.js b/src/components/app/actionTypes.js
new file mode 100644
index 0000000..92b4d83
--- /dev/null
+++ b/src/components/app/actionTypes.js
@@ -0,0 +1,22 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+export const START = 'start';
+export const PLAY = 'play';
+export const SHOW_NEW_GAME = 'show-new-game';
+export const SHOW_JOIN_GAME = 'show-join-game';
diff --git a/src/components/app/actions.js b/src/components/app/actions.js
new file mode 100644
index 0000000..797d285
--- /dev/null
+++ b/src/components/app/actions.js
@@ -0,0 +1,37 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import { START, PLAY, SHOW_NEW_GAME, SHOW_JOIN_GAME } from './actionTypes.js'
+
+export { start, play, showNewGame, showJoinGame }
+
+function start() {
+ return { type: START };
+}
+
+function play() {
+ return { type: PLAY };
+}
+
+function showNewGame() {
+ return { type: SHOW_NEW_GAME };
+}
+
+function showJoinGame() {
+ return { type: SHOW_JOIN_GAME };
+}
diff --git a/src/components/app/reducers.js b/src/components/app/reducers.js
new file mode 100644
index 0000000..647f3bd
--- /dev/null
+++ b/src/components/app/reducers.js
@@ -0,0 +1,40 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import { PLAY, SHOW_NEW_GAME, SHOW_JOIN_GAME, START } from './actionTypes.js'
+import { SCREENS } from '../../constants.js'
+
+const initialState = {
+ screen: SCREENS.intro
+};
+
+export default function(state = initialState, action) {
+ switch (action.type) {
+ case START:
+ return { ...state, screen: SCREENS.start };
+ case PLAY:
+ return { ...state, screen: SCREENS.play };
+ case SHOW_NEW_GAME:
+ return { ...state, screen: SCREENS.newGame };
+ case SHOW_JOIN_GAME:
+ return { ...state, screen: SCREENS.joinGame };
+ default:
+ return state;
+ }
+}
+
diff --git a/src/components/create-or-join/CreateOrJoin.jsx b/src/components/create-or-join/CreateOrJoin.jsx
new file mode 100644
index 0000000..cdc6b03
--- /dev/null
+++ b/src/components/create-or-join/CreateOrJoin.jsx
@@ -0,0 +1,45 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React, { Fragment } from 'react'
+import { connect } from 'react-redux'
+
+import { GroupBox, Row, Col, Button } from '../widgets.jsx'
+import { showNewGame, showJoinGame } from '../app/actions.js'
+
+class CreateOrJoin extends React.Component {
+ render() {
+ return (
+ <Fragment>
+ <Button size='large' className='shadow' onClick={this.props.showNewGame}>
+ New Game
+ </Button>
+ <Button size='large' className='shadow' onClick={this.props.showJoinGame}>
+ Join Game
+ </Button>
+ </Fragment>
+ );
+ }
+}
+
+export default connect(
+ state => state,
+ { showNewGame,
+ showJoinGame
+ }
+)(CreateOrJoin)
diff --git a/src/components/farm/Board.jsx b/src/components/farm/Board.jsx
new file mode 100644
index 0000000..963178b
--- /dev/null
+++ b/src/components/farm/Board.jsx
@@ -0,0 +1,1446 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import CornImg from './../../../assets/img/corn.svg'
+import FruitImg from './../../../assets/img/fruit.svg'
+import CowImg from './../../../assets/img/cow.svg'
+import HayImg from './../../../assets/img/hay.svg'
+import WheatImg from './../../../assets/img/wheat.svg'
+import TractorImg from './../../../assets/img/tractor-icon.svg'
+import TractorFullImg from './../../../assets/img/tractor-with-spikes.svg'
+import HarvesterImg from './../../../assets/img/harvester.svg'
+
+import React, { Fragment } from 'react'
+import ReactDOM from 'react-dom'
+import { connect } from 'react-redux'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import { faUser, faUsers, faTractor, faWindowRestore,
+ faDollarSign, faTimes, faAsterisk
+ } from '@fortawesome/free-solid-svg-icons'
+
+import { GroupBox, Row, Col, Button } from '../widgets.jsx'
+import SpaceNode from './SpaceNode.jsx'
+import Tractor from '../tractor/Tractor.jsx'
+
+import { GAME_STATES, ALERTS } from '../../constants.js'
+import { itemCard, itemCardShort, fateCard, ridgeNames } from 'game.js'
+import { setSelectedCard, setMessagePanelSpace, setMPDims, movePlayer,
+ nextUIAction, alert } from './actions.js'
+import { buy, roll, endTurn, loan, trade, submitTradeAccept,
+ submitTradeDeny, submitTradeCancel, audit,
+ buyUncleBert } from './interface.js'
+
+function netWorth(player) {
+ return ((player.assets.hay + player.assets.grain) * 2000) +
+ (player.assets.fruit * 5000) +
+ (player.assets.cows * 500) +
+ ((player.assets.harvester + player.assets.tractor) * 10000) +
+ player.cash - player.debt;
+}
+
+function assetsValue(player) {
+ return ((player.assets.hay + player.assets.grain) * 2000) +
+ (player.assets.fruit * 5000) +
+ (player.assets.cows * 500) +
+ ((player.assets.harvester + player.assets.tractor) * 10000);
+}
+
+function getElementValue(id) {
+ return document.getElementById(id).value;
+}
+
+function getString(id) {
+ return getElementValue(id);
+}
+
+function getInt(id) {
+ return parseInt(getElementValue(id));
+}
+
+function getChecked(id) {
+ return document.getElementById(id).checked;
+}
+
+function getOption(id) {
+ let options = document.getElementById(id).selectedOptions;
+ if (options.length > 0) {
+ return options[0].value;
+ } else {
+ return false;
+ }
+}
+
+function submitTrade() {
+ trade({ hay: getInt('trade-hay'), grain: getInt('trade-grain'),
+ fruit: getInt('trade-fruit'), cows: getInt('trade-cows'),
+ harvester: getInt('trade-harvesters'),
+ tractor: getInt('trade-tractors'),
+ ridge1: getChecked('trade-ridge1'),
+ ridge2: getChecked('trade-ridge2'),
+ ridge3: getChecked('trade-ridge3'),
+ ridge4: getChecked('trade-ridge4'),
+ player: getOption('trade-player'),
+ money: getInt('trade-money'), cards: getString('trade-cards') });
+}
+
+function tradeString(player, invert) {
+ var r = '';
+ let mult = invert ? -1 : 1;
+ for (var k in player.trade) {
+ if (k !== 'player' && k !== 'originator' && k !== 'cards') {
+ r += (player.trade[k] === true ? '' : player.trade[k] * mult) + ' ' + k + ', ';
+ } else if (k === 'cards') {
+ r += 'cards: ' + player.trade[k] + ', ';
+ }
+ }
+ return r.slice(0, -2); // remove last ", "
+}
+
+class PlayerTradeProposed extends React.Component {
+ render () {
+ if (this.props.player.trade.player === undefined) {
+ return (<br />);
+ } else if (this.props.player.trade.originator === this.props.player.name) {
+ return (<p>You proposed a trade with <b>{this.props.player.trade.player}</b> for
+ {'\u00A0'}<b>{tradeString(this.props.player, false)}</b>.</p>);
+ } else {
+ return (<p><b>{this.props.player.trade.player}</b> proposed a trade for
+ {'\u00A0'}<b>{tradeString(this.props.player, true)}</b>.</p>);
+ }
+ }
+}
+
+function tradeFormSubmit(e, player) {
+ e.preventDefault();
+ if (player.trade.player === undefined) {
+ submitTrade();
+ } else if (player.trade.originator === player.name) {
+ submitTradeCancel();
+ } else {
+ submitTradeAccept();
+ }
+ return false;
+}
+
+class PlayerTradeButton extends React.Component {
+ tradeDeny = e => {
+ e.preventDefault();
+ submitTradeDeny();
+ }
+
+ render() {
+ var text = '';
+ var receiver = false;
+ if (this.props.player.trade.player === undefined) {
+ text = 'Propose';
+ } else if (this.props.player.trade.originator === this.props.player.name) {
+ text = 'Cancel';
+ } else {
+ text = 'Accept';
+ receiver = true;
+ }
+ if (!receiver) {
+ return (<Button className='tiny' type='submit'>{text} Trade</Button>);
+ } else {
+ return (<Fragment>
+ <Button className='tiny' type='submit'>{text} Trade</Button>
+ <Button className='tiny' onClick={this.submitTradeDeny}>
+ Deny Trade
+ </Button>
+ </Fragment>);
+ }
+ }
+}
+
+class ResourceUnit extends React.Component {
+ render () {
+ const hslString = 'hsl(' + this.props.h + ', ' + this.props.s;
+ return (
+ <div className='resource-unit'
+ title={this.props.amount + ' ' + this.props.label}
+ style={{backgroundColor: hslString + '%, 85%)',
+ borderTop: '3px solid ' + hslString + '%, 55%)'}}>
+ {this.props.img ? (<img src={this.props.img} />) : (<Fragment />)}
+ {this.props.children}
+ </div>
+ );
+ }
+}
+
+class PlayerTradeResources extends React.Component {
+ render () {
+ let player = this.props.player;
+ return (<form onSubmit={e => tradeFormSubmit(e, player)}>
+<div className='player-trade-resources'>
+ <ResourceUnit h='120' s='100' label='acres of Hay' amount={''}>
+ <input type='number' id='trade-hay' defaultValue='0' /></ResourceUnit> {' '}
+ <ResourceUnit h='41' s='100' label='acres of Grain' amount={''}>
+ <input type='number' id='trade-grain' defaultValue='0' /></ResourceUnit> {' '}
+ <ResourceUnit h='0' s='100' label='acres of Fruit' amount={''}>
+ <input type='number' id='trade-fruit' defaultValue='0' /></ResourceUnit> {' '}
+ <ResourceUnit h='0' s='59' label='head of Cows' amount={''}>
+ <input type='number' id='trade-cows' defaultValue='0' /></ResourceUnit> {' '}
+ <ResourceUnit h='240' s='100' label='Harvesters' amount={''}>
+ <input type='number' id='trade-harvesters' defaultValue='0' /></ResourceUnit> {' '}
+ <ResourceUnit h='240' s='100' label='Tractors' amount={''}>
+ <input type='number' id='trade-tractors' defaultValue='0' /></ResourceUnit>
+ <br /><br />
+ <b>Ridges</b>: <b>{ridgeNames[0][0]}</b>: <input type='checkbox' id='trade-ridge1' />{'\u00A0'}
+ <b>R</b>: <input type='checkbox' id='trade-ridge2' />{'\u00A0'}
+ <b>C</b>: <input type='checkbox' id='trade-ridge3' />{'\u00A0'}
+ <b>T</b>: <input type='checkbox' id='trade-ridge4' />{'\u00A0'}
+ <br /><br />
+ <Row>
+ <Col width='4 column-no-padding'>
+ <label htmlFor='trade-player'><b>Player</b>:</label>
+ <select id='trade-player'>
+ {this.props.otherPlayers.map(p => (
+ <option key={p.player.name} value={p.player.name}>{p.player.name}</option>
+ ))}
+ </select>
+ </Col>
+ <Col width='4 column-no-padding'>
+ <label htmlFor='trade-money'><b>Money</b>: </label>
+ <input type='number' id='trade-money' defaultValue='0' />
+ </Col>
+ <Col width='4 column-no-padding'>
+ <label htmlFor='trade-cards'><b>Cards</b>: </label>
+ <input type='text' id='trade-cards' />
+ </Col>
+ </Row>
+ <PlayerTradeProposed player={this.props.player} />
+ <PlayerTradeButton player={this.props.player} />
+</div>
+</form>);
+ }
+}
+
+class PlayerResources extends React.Component {
+ render () {
+ const player = this.props.player;
+ return (
+ <div className='resource-unit-container'>
+ <ResourceUnit img={HayImg} h='120' s='100' label='acres of Hay'
+ amount={player.assets.hay}>
+ {player.assets.hay}
+ </ResourceUnit> {' '}
+ <ResourceUnit img={WheatImg} h='41' s='100' label='acres of Grain'
+ amount={player.assets.grain}>
+ {player.assets.grain}
+ </ResourceUnit> {' '}
+ <ResourceUnit img={FruitImg} h='0' s='100' label='acres of Fruit'
+ amount={player.assets.fruit}>
+ {player.assets.fruit}
+ </ResourceUnit> {' '}
+ <ResourceUnit img={CowImg} h='0' s='59' label='head of Cows'
+ amount={player.assets.cows}>
+ {player.assets.cows}
+ </ResourceUnit> {' '}
+ <ResourceUnit img={HarvesterImg} h='240' s='100' label='Harvesters'
+ amount={player.assets.harvester}>
+ {player.assets.harvester}
+ </ResourceUnit> {' '}
+ <ResourceUnit img={TractorImg} h='240' s='100' label='Tractors'
+ amount={player.assets.tractor}>
+ {player.assets.tractor}
+ </ResourceUnit>
+ </div>
+ );
+ }
+}
+
+// http://stackoverflow.com/questions/149055
+function formatMoney(n) {
+ return n.toFixed(1).replace(/(\d)(?=(\d{3})+\.)/g, '$1,').slice(0, -2); }
+
+class MoneySummary extends React.Component {
+ render () {
+ return (
+ <span><b>{this.props.title}</b>: ${formatMoney(this.props.value)}</span>
+ );
+ }
+}
+
+class PlayerSummary extends React.Component {
+ render () {
+ const player = this.props.player;
+ return (
+ <GroupBox title={(<Fragment><div className={'player player-' + player.color}></div> {player.name}
+ </Fragment>)}>
+ <MoneySummary title='Cash' value={player.cash} /> {' '}
+ <MoneySummary title='Debt' value={player.debt} /> {' '}
+ <br /><br />
+ <MoneySummary title='Assets' value={assetsValue(player)} /> {' '}
+ <MoneySummary title='Net worth' value={netWorth(player)} />
+ <br /><br />
+ <PlayerResources player={player} />
+ <br />
+ <PlayerTurnContainer player={player}
+ game={this.props.game}
+ ui={this.props.ui}
+ screen={this.props.screen}
+ showScreen={this.props.showScreen}/>
+ </GroupBox>
+ );
+ }
+}
+
+class PlayerTurnContainer extends React.Component {
+ clickRoll = () => {
+ roll();
+ this.props.showScreen(SCREENS.action);
+ }
+
+ render() {
+ let view;
+ const player = this.props.player,
+ worth = netWorth(player),
+ auditButton = (this.props.game.calledAudit === false && worth >= this.props.game.auditThreshold) ? (
+ <Button onClick={audit}>Call Audit</Button>) : '';
+ if (player.state === GAME_STATES.preTurn) {
+ view = (
+ <Fragment>
+ <Button onClick={this.clickRoll}>Roll</Button>{' '}{auditButton}
+ </Fragment>
+ );
+ } else if (player.state === GAME_STATES.midTurn) {
+ if (this.props.ui.action) {
+ if (this.props.screen === SCREENS.action) {
+ view = `It's your turn!`;
+ } else {
+ view = (
+ <Button onClick={() => this.props.showScreen(SCREENS.action)}>
+ Complete your turn
+ </Button>);
+ }
+ } else {
+ if (player.cash < 0) {
+ view = (
+ <Button onClick={() => this.props.showScreen(SCREENS.loans)}>
+ Raise Cash
+ </Button>
+ );
+ } else {
+ view = (
+ <Fragment>
+ <Button onClick={endTurn}>
+ End Turn
+ </Button>{' '}{auditButton}
+ </Fragment>);
+ }
+ }
+ } else if (this.props.game.state === 'finished') {
+ view = (<b>Game finished!</b>);
+ } else {
+ view = (
+ <Button onClick={() => this.props.showScreen(SCREENS.action)}>
+ Watch {this.props.game.currentPlayer}&apos;s turn
+ </Button>);
+ }
+ return (
+ <Row>
+ <Col width='12'>
+ <div className='turn-container'>
+ {view}
+ </div>
+ </Col>
+ </Row>
+ );
+ }
+}
+
+// just the circle, for players circles in a space see PlayerIcon
+class PlayerColorIcon extends React.Component {
+ render() {
+ return (<div className={'player player-' + this.props.color}></div>);
+ }
+}
+
+function playerHasRidge(player, ridge) {
+ return player.ridges[ridge] !== 0 ? true : false;
+}
+
+function makeRidgeLookup(player, otherPlayers) {
+ var ridges = { ridge1: playerHasRidge(player, 'ridge1') ? player.color : false,
+ ridge2: playerHasRidge(player, 'ridge2') ? player.color : false,
+ ridge3: playerHasRidge(player, 'ridge3') ? player.color : false,
+ ridge4: playerHasRidge(player, 'ridge4') ? player.color : false };
+ for (let i = 0; i < otherPlayers.length; i++) {
+ let p = otherPlayers[i].player;
+ if (!ridges.ridge1) {
+ ridges.ridge1 = playerHasRidge(p, 'ridge1') ? p.color : false; }
+ if (!ridges.rattlensake) {
+ ridges.ridge2 = playerHasRidge(p, 'ridge2') ? p.color : false; }
+ if (!ridges.ridge3) {
+ ridges.ridge3 = playerHasRidge(p, 'ridge3') ? p.color : false; }
+ if (!ridges.ridge4) {
+ ridges.ridge4 = playerHasRidge(p, 'ridge4') ? p.color : false; }
+ }
+ return ridges;
+}
+
+class FarmsContainer extends React.Component {
+ render() {
+ let ridges = makeRidgeLookup(this.props.player, this.props.otherPlayers);
+ return (
+ <GroupBox title='Farms'>
+ <b>Ridges</b>: <b>{ridgeNames[0][0]}</b>: <PlayerColorIcon color={ridges.ridge1} />{'\u00A0'}
+ <b>{ridgeNames[1][0]}</b>: <PlayerColorIcon color={ridges.ridge2} />{'\u00A0'}
+ <b>{ridgeNames[2][0]}</b>: <PlayerColorIcon color={ridges.ridge3} />{'\u00A0'}
+ <b>{ridgeNames[3][0]}</b>: <PlayerColorIcon color={ridges.ridge4} />{'\u00A0'}
+ <br /><br />
+ {this.props.otherPlayers
+ .map(p => (
+ <div key={p.player.name}>
+ <PlayerColorIcon color={p.player.color} />{'\u00A0'}
+ <b>{p.player.name}</b> <PlayerResources player={p.player} />
+ <br /> <br />
+ </div>))}
+ </GroupBox>
+ );
+ }
+}
+
+class TradeContainer extends React.Component {
+ render() {
+ return (
+ <GroupBox title='Trade'>
+ <PlayerTradeResources otherPlayers={this.props.game.otherPlayers}
+ player={this.props.player} />
+ </GroupBox>
+ );
+ }
+}
+
+class CCBY extends React.Component {
+ render() {
+ return (
+ <Fragment>
+ License Creative Commons <a href='https://creativecommons.org/licenses/by/3.0/us/legalcode'>CCBY</a>
+ </Fragment>
+ );
+ }
+}
+
+class Misc extends React.Component {
+ render() {
+ return (
+ <div className='credits'>
+ <h1>Credits</h1>
+ <ul>
+ <li>
+ Game created by <a href='https://thomashintz.org'>Thomas Hintz</a>.
+ </li>
+ <li>
+ <a href='https://code.thintz.com/farm'>Game source</a> available under the <a href='https://www.gnu.org/licenses/gpl-3.0-standalone.html'>GPLv3</a>+ license.
+ </li>
+ <li>
+ <img src={TractorFullImg} /> <img src={TractorImg} /> Copyright Nick Roach with modifications by Thomas Hintz - License <a href='GPL http://www.gnu.org/copyleft/gpl.html'>GPL</a>
+ </li>
+ <li>
+ <img src={CornImg} /> <img src={FruitImg} /> Copyright <a href='https://madexmade.com/'>Made</a> - <CCBY />
+ </li>
+ <li>
+ <img src={CowImg} /> Copyright <a href='https://thenounproject.com/rivercon/'>rivercon</a> - <CCBY />
+ </li>
+ <li>
+ <img src={HayImg} /> Copyright <a href='https://thenounproject.com/maxicons/'>Maxicons</a> - <CCBY />
+ </li>
+ <li>
+ <img src={WheatImg} /> Copyright <a href='https://thenounproject.com/amoghdesign/'>P Thanga Vignesh</a> - <CCBY />
+ </li>
+ <li>
+ <img src={HarvesterImg} /> Copyright <a href='https://andrejskirma.com/'>Andrejs Kirma</a> - <CCBY />
+ </li>
+ </ul>
+ </div>
+ );
+ }
+}
+
+class Messages extends React.Component {
+ render() {
+ let lastTurn = -1,
+ newTurn = false;
+ return (
+ <div className='messages'>
+ {this.props.game.messages.map((m, i) => (
+ <span key={i}>{m[0] === this.props.player.name ? (<b>{m[0]}</b>) : m[0]}{': ' + m[2]}<br /></span>
+ ))}
+ {this.props.game.messages.length > 0 ? (<Fragment><br /><br /></Fragment>) : (<Fragment />)}
+ {this.props.game.oldMessages.map((m, i) => {
+ newTurn = false;
+ if (m[1] !== lastTurn) {
+ lastTurn = m[1];
+ newTurn = true;
+ }
+ return (
+ <Fragment key={i}>
+ {newTurn ? (<Fragment><b>Turn {m[1]}</b><br /></Fragment>) : <Fragment />}
+ <span>
+ {m[0] + ': ' + m[2]}
+ <br /></span>
+ </Fragment>
+ )})}
+ </div>
+ );
+ }
+}
+
+class Loans extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { repay: 0, takeOut: 0 };
+ }
+
+ handleInput = e => {
+ const target = e.target,
+ name = e.target.name;
+ let value = Math.max(0, parseInt(e.target.value));
+ if (isNaN(value)) { value = 0; }
+ let repay = 0,
+ takeOut = 0;
+ if (name === 'repay') {
+ repay = value * 1000;
+ } else {
+ takeOut = value * 1000;
+ }
+ this.setState({ repay: Math.max(0, Math.floor(
+ Math.min(repay, this.props.player.debt, this.props.player.cash) / 1000)),
+ takeOut: Math.max(0, Math.floor(
+ Math.min(takeOut, 50000 - this.props.player.debt) / 1000)) });
+ }
+
+ handleSubmit = e => {
+ e.preventDefault();
+ loan((this.state.repay + this.state.takeOut) * (this.state.repay > 0 ? -1 : 1));
+ this.setState({ repay: 0, takeOut: 0 });
+ }
+
+ render () {
+ return (
+ <GroupBox title={'Loans'}>
+ <MoneySummary title='Cash' value={this.props.player.cash} /> {' '}
+ <MoneySummary title='Debt' value={this.props.player.debt} />/$50,000
+ <br /><br />
+ <form onSubmit={this.handleSubmit}>
+ <Row collapse='true'>
+ <Col width='8'>
+ <div className='money'>
+ $:
+ <input onChange={this.handleInput} name='repay' type='number'
+ value={this.state.repay === 0 ? '' : this.state.repay}/>
+ {'\u00A0'}K / ${this.props.player.debt / 1000}K
+ </div>
+ </Col>
+ <Col width='4'>
+ <Button className='tiny' type='submit'>Repay</Button>
+ </Col>
+ </Row>
+ <Row collapse='true'>
+ <Col width='8'>
+ <div className='money'>
+ $:
+ <input onChange={this.handleInput} name='takeOut' type='number'
+ value={this.state.takeOut === 0 ? '' : this.state.takeOut} />
+ {'\u00A0'}K / ${(50000 - this.props.player.debt) / 1000}K
+ </div>
+ </Col>
+ <Col width='4'>
+ <Button className='tiny' type='submit'>Take Out</Button>
+ </Col>
+ </Row>
+ </form>
+ </GroupBox>
+ );
+ }
+}
+
+function random(max, min = 1) {
+ return Math.floor(Math.random() * max) + min;
+}
+
+function decay(totalMs, decayFactorPct, ticksToGo) {
+ return totalMs * Math.pow(1 - decayFactorPct, ticksToGo);
+}
+
+class Die extends React.Component {
+ timerId = false;
+ lastRoll = 0;
+ ticks = 0;
+ maxTicks = Number.MAX_SAFE_INTEGER;
+ speed = { fast: 0.1, slow: 0.7 };
+ interval = 100;
+ acc = 0;
+ trigger = 0;
+ decayFactorPct = 0.4;
+ showScreenTimerId = false;
+
+ constructor(props) {
+ super(props);
+ this.state = { num: props.roll ? this.roll() : props.num,
+ finalFace: false }
+ this.trigger = 1000 * this.speed[props.speed ? props.speed : 'fast']
+ if (props.ms) {
+ this.maxTicks = Math.floor(props.ms / this.trigger);
+ if (props.decay) {
+ this.trigger = decay(props.ms, this.decayFactorPct, this.maxTicks);
+ }
+ }
+ }
+
+ roll() {
+ let roll = random(6);
+ while (roll === this.lastRoll) {
+ roll = random(6);
+ }
+ this.lastRoll = roll;
+ return roll;
+ }
+
+ tick() {
+ this.acc += this.interval;
+ // update die face
+ if (this.ticks < this.maxTicks && this.acc >= this.trigger) {
+ this.ticks++;
+ this.acc = 0;
+ // update trigger for decay
+ if (this.props.decay) {
+ this.trigger = decay(this.props.ms, this.decayFactorPct,
+ this.maxTicks - this.ticks);
+ }
+ // we're finished, clear things, set final roll value
+ if (this.ticks >= this.maxTicks) {
+ clearInterval(this.timerId);
+ this.timerId = false;
+ this.setState({ num: this.props.num, finalFace: true });
+ if (this.props.showScreen) {
+ this.showScreenTimerId = setInterval(() => {
+ this.props.showScreen();
+ clearInterval(this.showScreenTimerId);
+ this.showScreenTimerId = false;
+ }, this.props.showScreenDelay);
+ }
+ } else {
+ this.setState({ num: this.roll() });
+ }
+ }
+ }
+
+ componentDidMount() {
+ if (this.props.roll) {
+ this.timerId = setInterval(() => this.tick(), this.interval);
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.timerId) {
+ clearInterval(this.timerId);
+ }
+ if (this.showScreenTimerId) {
+ clearInterval(this.showScreenTimerId);
+ }
+ }
+
+ skip() {
+ clearInterval(this.timerId);
+ this.timerId = false;
+ this.setState({ num: this.props.num, finalFace: true });
+ if (this.props.showScreen) {
+ this.showScreenTimerId = setInterval(() => {
+ this.props.showScreen();
+ clearInterval(this.showScreenTimerId);
+ this.showScreenTimerId = false;
+ }, this.props.showScreenDelay);
+ }
+ }
+
+ render() {
+ let face;
+ switch (this.state.num) {
+ case 1:
+ face = (<div className='die-face face-1'></div>);
+ break;
+ case 2:
+ face = (<Fragment>
+ <div className='die-face face-21'></div>
+ <div className='die-face face-22'></div>
+ </Fragment>);
+ break;
+ case 3:
+ face = (<Fragment>
+ <div className='die-face face-21'></div>
+ <div className='die-face face-1'></div>
+ <div className='die-face face-22'></div>
+ </Fragment>);
+ break;
+ case 4:
+ face = (<Fragment>
+ <div className='die-face face-21'></div>
+ <div className='die-face face-22'></div>
+ <div className='die-face face-52'></div>
+ <div className='die-face face-54'></div>
+ </Fragment>);
+ break;
+ case 5:
+ face = (<Fragment>
+ <div className='die-face face-21'></div>
+ <div className='die-face face-1'></div>
+ <div className='die-face face-22'></div>
+ <div className='die-face face-52'></div>
+ <div className='die-face face-54'></div>
+ </Fragment>);
+ break;
+ case 6:
+ face = (<Fragment>
+ <div className='die-face face-21'></div>
+ <div className='die-face face-63'></div>
+ <div className='die-face face-64'></div>
+ <div className='die-face face-22'></div>
+ <div className='die-face face-52'></div>
+ <div className='die-face face-54'></div>
+ </Fragment>);
+ break;
+ }
+ return (
+ <Fragment>
+ <div className={'die' + (this.state.finalFace ? ' die-selected' : '')}>
+ {face}
+ </div>
+ {this.props.skip && !this.state.finalFace ?
+ (<div className='center'>
+ <Button onClick={() => this.skip()}>Skip</Button>
+ </div>) :
+ (<Fragment />)}
+ </Fragment>
+ );
+ }
+}
+
+class PreRolling extends React.Component {
+ render() {
+ return (
+ <GroupBox title={this.props.name + ' is rolling!'}>
+ <Die roll={true} />
+ </GroupBox>
+ );
+ }
+}
+
+class Rolling extends React.Component {
+ render() {
+ return (
+ <GroupBox title={this.props.name + ' is rolling!'}>
+ <Die decay={true} num={this.props.num} ms={2000} roll={true}
+ showScreen={this.props.showScreen}
+ skip={this.props.skip}
+ showScreenDelay={this.props.showScreenDelay} />
+ </GroupBox>
+ );
+ }
+}
+
+class Harvest extends React.Component {
+ cropsToImg = { hay: HayImg,
+ cherries: FruitImg,
+ wheat: WheatImg,
+ cows: CowImg,
+ fruit: FruitImg,
+ corn: CornImg }
+
+ constructor(props) {
+ super(props);
+ this.state = { view: 'ready' }
+ }
+
+ nextView = view => {
+ this.setState({ view });
+ }
+
+ cropToImg = crop => {
+ return this.cropsToImg[crop];
+ }
+
+ render() {
+ let view;
+ switch (this.state.view) {
+ case 'ready':
+ view = (
+ <div className='clear-background'>
+ <div className={'harvest-card space-type-' + this.props.crop}>
+ <h1>{this.props.crop + ' harvest!'}</h1>
+ <div className='harvest-card-contents'>
+ <img src={this.cropToImg(this.props.crop)} />
+ Get ready to harvest <b>{this.props.acres}
+ {this.props.crop === 'cows' ? ' head of cow' : ' acres'}</b>!
+ </div>
+ {this.props.player.name === this.props.game.currentPlayer ? (
+ <Button onClick={() => this.nextView('roll')}>
+ Roll for harvest!
+ </Button>
+ ) : (<Fragment />)}
+ </div>
+ </div>
+ );
+ break;
+ case 'roll':
+ view = (<Die decay={true} num={this.props.rolled} ms={2000} roll={true}
+ showScreen={() => this.nextView('income')}
+ skip={true}
+ showScreenDelay={2000} />);
+ break;
+ case 'income':
+ view = (
+ <div>
+ <div className='game-card'>
+ <div className={'flex green'}>
+ <FontAwesomeIcon icon={faDollarSign} size='6x' />
+ <div>
+ {this.props.player.name === this.props.game.currentPlayer ? 'You' :
+ this.props.game.currentPlayer} rolled a <b>{this.props.rolled}</b> and earned <b>${formatMoney(this.props.income)}</b>!
+ </div>
+ </div>
+ </div>
+ <div className='center spacer'>
+ <Button onClick={() => this.nextView('operating-expense')}>Draw Operating Expense</Button>
+ </div>
+ </div>
+ );
+ break;
+ case 'operating-expense':
+ view = (
+ <Fragment>
+ <div className='game-card'>
+ <GroupBox title='Operating Expense'>
+ <div className='card'
+ dangerouslySetInnerHTML={{__html: this.props.contents}} />
+ </GroupBox>
+ </div>
+ <div className='center spacer'>
+ <Button onClick={() => this.nextView('expense-value')}>Continue</Button>
+ </div>
+ </Fragment>
+ );
+ break;
+ case 'expense-value':
+ view = (
+ <div>
+ <div className='game-card'>
+ <div className={'flex ' + (this.props.expenseValue < 0 ? 'red' : 'green')}>
+ <FontAwesomeIcon icon={faDollarSign} size='6x' />
+ <div>
+ {this.props.player.name === this.props.game.currentPlayer ? 'You' :
+ this.props.game.currentPlayer} {this.props.expenseValue < 0 ? 'lost ' : 'gained '}
+ ${Math.abs(this.props.expenseValue)}!
+ </div>
+ </div>
+ </div>
+ <div className='center spacer'>
+ <Button onClick={this.props.nextAction}>Continue</Button>
+ </div>
+ </div>
+ );
+ break;
+ }
+ return view;
+ }
+}
+
+// props: currentPos, moveTo, color
+// actions: movePlayer
+class Moving extends React.Component {
+ timerId = false
+ endPos = 0
+ color = ''
+ hurtBack = false
+
+ constructor(props) {
+ super(props);
+ const to = this.props.ui.actionValue.to,
+ from = this.props.ui.actionValue.from;
+ const currentPlayer = this.props.player.name === this.props.game.currentPlayer ?
+ this.props.player : this.props.game.otherPlayers
+ .find(p => p.player.name === this.props.game.currentPlayer).player;
+ const currentPos = from;
+ this.state = { currentPos: currentPos < 0 ? currentPos + 49 : currentPos };
+ this.endPos = to;
+ this.color = currentPlayer.color;
+ // only for hurt back do we go backwards
+ this.hurtBack = to === 2 && from === 11 ? true : false;
+ }
+
+ componentDidMount() {
+ // TODO combine with tick
+ const delta = this.state.currentPos === 48 ? -48 : this.hurtBack ? -1 : 1;
+ this.props.movePlayer(this.state.currentPos + delta,
+ this.state.currentPos,
+ this.color);
+ this.setState(state => ({ currentPos: state.currentPos + delta}));
+ if (this.state.currentPos + delta !== this.endPos) {
+ this.timerId = setInterval(() => this.tick(), 500);
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.timerId) {
+ clearInterval(this.timerId);
+ this.timerId = false;
+ }
+ }
+
+ tick() {
+ const delta = this.state.currentPos === 48 ? -48 : this.hurtBack ? -1 : 1;
+ this.props.movePlayer(this.state.currentPos + delta,
+ this.state.currentPos,
+ this.color);
+ this.setState(state => ({ currentPos: state.currentPos + delta}));
+ if (this.state.currentPos === this.endPos) {
+ if (this.timerId) { clearInterval(this.timerId); }
+ this.timerId = false;
+ }
+ }
+
+ skip() {
+ clearInterval(this.timerId);
+ this.timerId = false;
+ this.props.movePlayer(this.endPos, this.state.currentPos, this.color);
+ this.setState({ currentPos: this.endPos });
+ }
+
+ render() {
+ let buttons;
+ if (this.props.player.name !== this.props.game.currentPlayer) {
+ buttons = (<Fragment />);
+ } else if (this.state.currentPos === this.endPos) {
+ buttons = (<Button onClick={() => this.props.showNextAction()}>Continue</Button>);
+ } else {
+ buttons = (<Button onClick={() => this.skip()}>Skip</Button>);
+ }
+ return (
+ <Row>
+ <Col width={'12'}>
+ <div className='moving'>
+ <div className={'action clear-background'}>
+ <SpaceNode space={this.props.spaces[this.state.currentPos]}
+ height={'170px'} showtitle={true} />
+ </div>
+ </div>
+ <br />
+ <div className={'center'}>
+ {buttons}
+ </div>
+ </Col>
+ </Row>
+ );
+ }
+}
+
+class Action extends React.Component {
+ render() {
+ let view, buttons;
+ const currentPlayer = this.props.player.name === this.props.game.currentPlayer ?
+ this.props.player : this.props.game.otherPlayers
+ .find(p => p.player.name === this.props.game.currentPlayer).player;
+ switch (this.props.ui.action) {
+ case 'otb':
+ view = (
+ <div className='game-card'>
+ <GroupBox title={itemCard}>
+ <div className='card'
+ dangerouslySetInnerHTML={{__html: this.props.ui.actionValue}} />
+ </GroupBox>
+ </div>
+ );
+ buttons = (<Button onClick={() => this.props.showNextAction()}>Continue</Button>);
+ break;
+ case 'farmers-fate':
+ view = (
+ <div className='game-card'>
+ <GroupBox title={fateCard}>
+ <div className='card'
+ dangerouslySetInnerHTML={{__html: this.props.ui.actionValue}} />
+ </GroupBox>
+ </div>
+ );
+ buttons = (<Button onClick={() => this.props.showNextAction()}>Continue</Button>);
+ break;
+ case 'ff-uncle-bert':
+ view = (
+ <GroupBox title={`Uncle Bert's inheritance`}>
+ <div className='center'>
+ <p>
+ {this.props.player.cash >= 10000 ?
+ `You have enough cash to take over Uncle Bert's farm!` :
+ `You must raise another $` +
+ formatMoney(10000 - this.props.player.cash) +
+ ` to be able to take over Uncle Berts farm!`
+ }
+ </p>
+ <p>
+ {this.props.player.cash >= 10000 ?
+ (<Button onClick={() => { buyUncleBert(); this.props.showNextAction(); }}>
+ Yes, take over for $10,000!</Button>) :
+ (<Fragment />)}
+ <Button onClick={() => this.props.showNextAction()}>
+ No, continue on
+ </Button>
+ </p>
+ </div>
+ </GroupBox>
+ );
+ buttons = (<Fragment />);
+ break;
+ case 'money':
+ view = (<div className='game-card'>
+ <div className={'flex ' + (this.props.ui.actionValue < 0 ? 'red' : 'green')}>
+ <FontAwesomeIcon icon={faDollarSign} size='6x' />
+ <div>
+ {this.props.player.name === this.props.game.currentPlayer ? 'You' :
+ this.props.game.currentPlayer} {this.props.ui.actionValue < 0 ? 'lost ' : 'gained '}
+ ${Math.abs(this.props.ui.actionValue)}!
+ </div>
+ </div>
+ </div>);
+ buttons = (<Button onClick={() => this.props.showNextAction()}>Continue</Button>);
+ break;
+ case 'info':
+ view = (
+ <div>
+ <p>{this.props.ui.actionValue}</p>
+ </div>
+ );
+ buttons = (<Button onClick={() => this.props.showNextAction()}>Continue</Button>);
+ break;
+ case 'harvest':
+ view = (<Harvest rolled={this.props.ui.actionValue.rolled}
+ player={this.props.player}
+ game={this.props.game}
+ income={this.props.ui.actionValue.income}
+ contents={this.props.ui.actionValue.operatingExpense}
+ expenseValue={this.props.ui.actionValue.operatingExpenseValue}
+ crop={this.props.ui.actionValue.crop}
+ acres={this.props.ui.actionValue.acres}
+ nextAction={() => this.props.showNextAction()} />);
+ buttons = (<Fragment />);
+ break;
+ case 'move':
+ view = (<Moving showNextAction={() => this.props.showNextAction()}
+ key={this.props.game.currentPlayer + '|' + this.props.ui.actionValue.from + '|' + this.props.ui.actionValue.to}
+ player={this.props.player}
+ game={this.props.game}
+ spaces={this.props.spaces}
+ movePlayer={this.props.movePlayer}
+ ui={this.props.ui}/>);
+ buttons = (<Fragment />);
+ break;
+ case 'goto':
+ view = (<Moving showNextAction={() => this.props.showNextAction()}
+ key={this.props.game.currentPlayer + '|' + this.props.ui.actionValue.from + '|' + this.props.ui.actionValue.to}
+ player={this.props.player}
+ game={this.props.game}
+ spaces={this.props.spaces}
+ movePlayer={this.props.movePlayer}
+ ui={this.props.ui} />);
+ buttons = (<Fragment />);
+ break;
+ case 'pre-rolling':
+ view = (<PreRolling name={this.props.game.currentPlayer} />);
+ buttons = (<Fragment />);
+ break;
+ case 'roll':
+ view = (<Rolling num={this.props.ui.actionValue.to - this.props.ui.actionValue.from}
+ name={this.props.game.currentPlayer}
+ showScreen={this.props.player.name === this.props.game.currentPlayer
+ ? () => this.props.showNextAction()
+ : false}
+ skip={this.props.player.name === this.props.game.currentPlayer}
+ showScreenDelay={2000} />);
+ buttons = (<Fragment />);
+ break;
+ default:
+ if (this.props.player.name === this.props.game.currentPlayer) {
+ view = (<PlayerSummary player={this.props.player}
+ ui={this.props.ui}
+ screen={this.props.screen}
+ showScreen={this.props.showScreen}
+ game={this.props.game} />);
+ } else {
+ view = (<Fragment>Waiting for {this.props.game.currentPlayer}</Fragment>);
+ }
+ }
+ return (
+ <Row>
+ <Col width={'12'}>
+ <div className={'action'}>
+ {view}
+ </div>
+ <br />
+ <div className={'center'}>
+ {this.props.otherPlayersTurn ? (<Fragment />) : buttons}
+ </div>
+ </Col>
+ </Row>
+ );
+ }
+}
+
+class Board extends React.Component {
+ render() {
+ const rh = this.props.height || '20px'; // height={h}
+ const renderSpace = (s, h, o) =>
+ (<SpaceNode space={s} key={s.key} orientation={o} />);
+
+ return (
+<Fragment>
+ <Row collapse='true' className='row-spaces'>
+ <Col width='1'>{renderSpace(this.props.spaces[0], rh, 'corner-tl')}</Col>
+ <Col width='10'>
+ <div className='flex-row'>
+ {this.props.spaces.slice(1, 14).map(s => renderSpace(s, rh, 'top'))}
+ </div>
+ </Col>
+ <Col width='1'>{renderSpace(this.props.spaces[14], rh, 'corner-tr')}</Col>
+ </Row>
+ <Row collapse='true' className='mid-row-container'>
+ <Col width='1'>
+ <div className='mid-col'>
+ {this.props.spaces.slice(38, 49).reverse()
+ .map(s => renderSpace(s, this.props.height || false, 'left'))}
+ </div>
+ </Col>
+ <Col width='10'>
+ <div className='mid-row'>
+ <div className='center-board'>
+ <Row>
+ <Col width='12 column-no-padding'>
+ {this.props.children}
+ </Col>
+ </Row>
+ <div className='board-heading'><h1>Alpha Centauri Farming</h1></div>
+ <Tractor />
+ </div>
+ </div>
+ </Col>
+ <Col width='1'>
+ <div className='mid-col'>
+ {this.props.spaces.slice(15, 25)
+ .map(s => renderSpace(s, this.props.height ? (parseInt(this.props.height) * 1.11) + 'px' : false, 'right'))}
+ </div>
+ </Col>
+ </Row>
+ <Row collapse='true' className='row-spaces'>
+ <Col width='1'>{renderSpace(this.props.spaces[37], rh, 'corner-bl')}</Col>
+ <Col width='10'>
+ <div className='flex-row'>
+ {this.props.spaces.slice(26, 37).reverse()
+ .map(s => renderSpace(s, rh, 'bottom'))}
+ </div>
+ </Col>
+ <Col width='1'>{renderSpace(this.props.spaces[25], rh, 'corner-br')}</Col>
+ </Row>
+ </Fragment>
+ );
+ }
+}
+
+// handler, buttonText, children
+class AlertOverlay extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { visible: typeof this.props.visible !== 'undefined' ?
+ this.props.visible : true };
+ }
+
+ hide = () => {
+ this.setState({ visible: false });
+ this.props.hideHandler();
+ }
+
+ buttonClick = () => {
+ this.hide();
+ this.props.handler();
+ }
+
+ render() {
+ return (
+ <div className={'alert-overlay' + (this.state.visible ? '' : ' hidden') }>
+ <div onClick={this.hide} className='alert-overlay-hide'>
+ <FontAwesomeIcon icon={faTimes} />
+ </div>
+ <div className='alert-overlay-contents'>
+ {this.props.children}
+ <br />
+ <Button onClick={this.buttonClick}>{this.props.buttonText}</Button>
+ <label><input type='checkbox' /> {`Don't show again`}</label>
+ <a href='#' onClick={this.hide}>close</a>
+ </div>
+ </div>
+ );
+ }
+}
+
+const SCREENS = { summary: 'summary', misc: 'misc', farms: 'farms',
+ cards: 'cards', trade: 'trade', loans: 'loans',
+ action: 'action' };
+
+class BoardApp extends React.Component {
+ iconToScreen = { user: SCREENS.summary, 'window-restore': SCREENS.cards,
+ 'dollar-sign': SCREENS.loans, users: SCREENS.farms,
+ 'exchange-alt': SCREENS.trade, asterisk: SCREENS.misc,
+ tractor: SCREENS.action }
+ unsubscribeAlert = () => null
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ screen: SCREENS.summary
+ };
+ }
+
+ showScreen = screen => {
+ this.setState({ screen: screen });
+ }
+
+ componentDidMount() {
+ // const midColHeight = (window.innerHeight -
+ // (document.getElementsByClassName('flex-row')[0].offsetHeight * 2)) + 'px';
+ // const midCols = document.getElementsByClassName('mid-col');
+ // midCols[0].style.height = midColHeight;
+ // midCols[1].style.height = midColHeight;
+
+ // const mpDims = this.props.mpDims;
+ // const minWidth = midCols[0].clientWidth;
+ // const minHeight = 78 + mpDims.padding;
+ // const maxHeight =
+ // midCols[0].clientHeight + minHeight - 225 - mpDims.padding;
+ // const maxWidth = window.innerWidth - minWidth - 160 - mpDims.padding;
+ // this.props.setMPDims(minWidth, minHeight, maxHeight, maxWidth);
+
+ // const midRow = document.getElementsByClassName('mid-row')[0];
+ // midRow.style.height = midColHeight;
+ // midRow.onmouseover = () => this.props.setMessagePanelSpace(null);
+ }
+
+ iconClass = icon => {
+ return this.iconToScreen[icon] === this.state.screen ? 'is-active' : ' ';
+ }
+
+ tabClass = screen => {
+ return 'tab' + (screen === this.state.screen ? ' show' : '');
+ }
+
+ iconOnClick = icon => {
+ return () => this.showScreen(this.iconToScreen[icon]);
+ }
+
+ render() {
+ let alertOverlay;
+ if (!this.props.ui.alertHandled && this.state.screen !== SCREENS.action) {
+ switch (this.props.ui.alert) {
+ case ALERTS.beginTurn:
+ alertOverlay = (
+ <AlertOverlay visible={true}
+ buttonText='Roll'
+ hideHandler={() => this.props.alert(false)}
+ handler={() => {
+ roll();
+ this.showScreen(SCREENS.action); }}>
+ <h1>{`It's your turn!`}</h1>
+ </AlertOverlay>
+ );
+ break;
+ case ALERTS.otherPlayersTurn:
+ alertOverlay = (
+ <AlertOverlay visible={true}
+ buttonText={'Watch ' + this.props.game.currentPlayer + `'s turn`}
+ hideHandler={() => this.props.alert(false)}
+ handler={() => {
+ this.showScreen(SCREENS.action); }}>
+ <h1>It&apos;s {this.props.game.currentPlayer}&apos;s turn</h1>
+ </AlertOverlay>
+ );
+ break;
+ default:
+ alertOverlay = (<Fragment />);
+ }
+ } else {
+ alertOverlay = (<Fragment />);
+ }
+ if (this.props.ui.alert === ALERTS.raiseMoney) {
+ alertOverlay = (
+ <AlertOverlay visible={true}
+ buttonText='Raise Money'
+ hideHandler={() => this.props.alert(false)}
+ handler={() => {
+ this.showScreen(SCREENS.loans); }}>
+ <Fragment>
+ <h1>Out of cash!</h1>
+ <p>You have less than $0 cash and must raise money to continue.</p>
+ </Fragment>
+ </AlertOverlay>
+ );
+ }
+ // faExchangeAlt -> trade icon, hidden for now
+ return (
+ <div className='game-container'>
+ {alertOverlay}
+ <Board spaces={this.props.spaces}>
+ <div className='center-board-container'>
+ <ul className='horizontal menu icons icons-top'>
+ {[faUser, faTractor, faWindowRestore, faDollarSign, faUsers, faAsterisk]
+ .map((icon, i) =>
+ (<li key={i} className={this.iconClass(icon.iconName)}>
+ <div></div>
+ <a href='#' onClick={this.iconOnClick(icon.iconName)}>
+ <FontAwesomeIcon icon={icon} /></a></li>))}
+ </ul>
+ <ul className='vertical menu icons icons-top'>
+ {[faUser, faTractor, faWindowRestore, faDollarSign, faUsers, faAsterisk]
+ .map((icon, i) =>
+ (<li key={i} className={this.iconClass(icon.iconName)}>
+ <a href='#' onClick={this.iconOnClick(icon.iconName)}>
+ <FontAwesomeIcon icon={icon} /></a></li>))}
+ </ul>
+ <div className='tab-container'>
+ <div className={this.tabClass(SCREENS.summary)}>
+ <PlayerSummary player={this.props.player}
+ ui={this.props.ui}
+ screen={this.state.screen}
+ game={this.props.game} showScreen={this.showScreen} />
+ </div>
+ <div className={this.tabClass(SCREENS.action)}>
+ <Action
+ spaces={this.props.spaces}
+ player={this.props.player}
+ game={this.props.game}
+ movePlayer={this.props.movePlayer}
+ showNextAction={this.props.nextUIAction}
+ otherPlayersTurn={this.props.player.name !== this.props.game.currentPlayer}
+ screen={this.state.screen}
+ showScreen={screen => this.showScreen(screen)}
+ ui={this.props.ui} />
+ </div>
+ <div className={this.tabClass(SCREENS.cards)}>
+ <Row>
+ <div className='cell medium-auto'>
+ <CardList ui={this.props.ui} />
+ </div>
+ <div className='cell medium-auto'>
+ <Card ui={this.props.ui} />
+ </div>
+ </Row>
+ </div>
+ <div className={this.tabClass(SCREENS.loans)}>
+ <Loans player={this.props.player} />
+ </div>
+ <div className={this.tabClass(SCREENS.farms)}>
+ <FarmsContainer player={this.props.player}
+ otherPlayers={this.props.game.otherPlayers} />
+ </div>
+ <div className={this.tabClass(SCREENS.trade)}>
+ <TradeContainer player={this.props.player} game={this.props.game} />
+ </div>
+ <div className={this.tabClass(SCREENS.misc)}>
+ <Misc />
+ </div>
+ </div>
+ </div>
+ </Board>
+ </div>
+ );
+ }
+}
+
+export default connect(
+ state => state.farm,
+ { setMessagePanelSpace, setMPDims, nextUIAction, movePlayer, alert }
+)(BoardApp)
+
+class Card extends React.Component {
+ render () {
+ const card = this.props.ui.card;
+ let action = (null);
+ switch (card.type) {
+ case 'otb': action = (
+ <div className='card-action'>
+ <form onSubmit={(e) =>
+ { e.preventDefault();
+ buy(card.id, parseInt(document.getElementById('cash-input').value));
+ return false; }}>
+ <Row collapse='true'>
+ <Col width='2'>
+ <Button className='tiny' type='submit'>Buy</Button>
+ </Col>
+ <Col width='4' />
+ <Col width='6'>
+ <div className='money'>
+ $:
+ <input id='cash-input' type='number' />
+ {'\u00A0'},000
+ </div>
+ </Col>
+ </Row>
+ </form>
+ </div>); break;
+ case 'no-card': action =
+ (<span></span>); break;
+ }
+ return (
+ <div className='game-card'>
+ <GroupBox title={this.props.ui.card.title}>
+ <div className='card-id'>{card.id}</div>
+ <div className='card'
+ dangerouslySetInnerHTML={{__html: card.contents}} />
+ {this.props.hideBuying ? (<Fragment />) : action}
+ </GroupBox>
+ </div>
+ );
+ }
+}
+
+// (<option key={i}>{typeToText(c.type)}: {cropToText(c.crop)}</option>))
+
+class CardListComp extends React.Component {
+ render () {
+ const typeToText = type => { switch (type) {
+ case 'otb': return itemCardShort }},
+ cropToText = crop => { switch (crop) {
+ case 'fruit': return '5 acres Fruit';
+ case 'grain': return '10 acres Grain'}};
+ const ui = this.props.ui,
+ cards = ui.cards,
+ cardOps = cards.map((c, i) =>
+ (<li key={i} className={c == ui.card ? 'card-select-selected' : ''}
+ onClick={() => this.props.setSelectedCard(c)}>
+ {c.summary}
+ </li>));
+
+ return (
+ <GroupBox title='Cards'>
+ <ul className='card-select'>
+ {cardOps}
+ </ul>
+ </GroupBox>
+ );
+ }
+}
+
+const CardList = connect(
+ null,
+ { setSelectedCard }
+)(CardListComp)
diff --git a/src/components/farm/MessagePanel.jsx b/src/components/farm/MessagePanel.jsx
new file mode 100644
index 0000000..19fde7f
--- /dev/null
+++ b/src/components/farm/MessagePanel.jsx
@@ -0,0 +1,50 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React from 'react'
+import { connect } from 'react-redux'
+
+import SpaceNode from './SpaceNode.jsx'
+
+import { setMessagePanelSpace, mpMouse } from './actions.js'
+
+class MessagePanel extends React.Component {
+ render () {
+ if (this.props.space !== null) {
+ const panel = document.getElementById('message-panel'),
+ mpDims = this.props.mpDims;
+ panel.style.top =
+ (Math.min(Math.max(mpDims.mouseY, mpDims.minHeight + mpDims.padding),
+ mpDims.maxHeight)) + 'px';
+ panel.style.left =
+ (Math.min(Math.max(mpDims.mouseX, mpDims.minWidth + mpDims.padding),
+ mpDims.maxWidth)) + 'px';
+ return (
+ <SpaceNode space={this.props.space} height='210px'
+ showtitle={true} orientation={''} />
+ );
+ } else {
+ return null;
+ }
+ }
+}
+
+export default connect(
+ state => state.farm,
+ null
+)(MessagePanel);
diff --git a/src/components/farm/PlayerIcon.jsx b/src/components/farm/PlayerIcon.jsx
new file mode 100644
index 0000000..751faf0
--- /dev/null
+++ b/src/components/farm/PlayerIcon.jsx
@@ -0,0 +1,30 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React from 'react'
+
+export default class PlayerIcon extends React.Component {
+ render() {
+ return (
+ <center>
+ {this.props.colors
+ .map(c => (<div key={c} className={'player player-' + c}></div>))}
+ </center>
+ );
+ }
+}
diff --git a/src/components/farm/SpaceNode.jsx b/src/components/farm/SpaceNode.jsx
new file mode 100644
index 0000000..44a72f3
--- /dev/null
+++ b/src/components/farm/SpaceNode.jsx
@@ -0,0 +1,69 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React from 'react'
+import { connect } from 'react-redux'
+
+import PlayerIcon from './PlayerIcon.jsx'
+
+import { setMessagePanelSpace, mpMouse } from './actions.js'
+
+class SpaceNode extends React.Component {
+ render() {
+ const space = this.props.space;
+ let title = '';
+ if (this.props.showtitle) {
+ switch (this.props.space.type) {
+ case 'hay': title = 'Hay Cutting'; break;
+ case 'cherry': title = 'Cherry Harvest'; break;
+ case 'wheat': title = 'Wheat Harvest'; break;
+ case 'cows': title = 'Livestock Sales'; break;
+ case 'apple': title = 'Apple Harvest'; break;
+ case 'corn': title = 'Corn Harvest'; break;
+ case 'buy': title = 'Purchasing'; break;
+ }
+ }
+ return (
+ <div className={'space space-type-' + this.props.space.type +
+ ' space-orientation-' + this.props.orientation}
+ onMouseOver={evt => {
+ const clientRects = evt.target.getClientRects()[0];
+ this.props.setMessagePanelSpace(space);
+ this.props.mpMouse(clientRects.left, clientRects.top);
+ return false; } }>
+ <div style={this.props.height ? {height: this.props.height} : {}}>
+ <center>{this.props.space.month}</center>
+ { this.props.showtitle ? (
+ <div className='space-title'>
+ {title}
+ </div>)
+ : (null)}
+ { this.props.space.players.length ? <PlayerIcon colors={this.props.space.players} /> : ''}
+ <div className='space-description'>
+ {this.props.space.description}
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
+export default connect(
+ null,
+ { setMessagePanelSpace, mpMouse }
+)(SpaceNode)
diff --git a/src/components/farm/actionTypes.js b/src/components/farm/actionTypes.js
new file mode 100644
index 0000000..8c4f164
--- /dev/null
+++ b/src/components/farm/actionTypes.js
@@ -0,0 +1,36 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+export const UPDATE_GAME = 'update-game';
+export const UPDATE_PLAYER = 'update-player';
+export const GAME_STATE = 'game-state';
+export const SET_SELECTED_CARD = 'set-selected-card';
+export const SET_CARDS = 'set-cards';
+export const SPACE_PUSH_PLAYER = 'space-push-player';
+export const SPACE_CLEAR_PLAYERS = 'space-clear-players';
+export const SET_OLD_MESSAGES = 'set-old-messages';
+export const MESSAGE_PANEL_SPACE = 'message-panel-space';
+export const MP_MOUSE = 'mp-mouse';
+export const SET_MP_DIMS = 'set-mp-dims';
+export const MOVE_PLAYER = 'move-player'
+export const SET_NEXT_ACTION = 'set-next-action'
+export const NEXT_UI_ACTION = 'next-ui-action'
+export const NEXT_UI_ACTION_SILENT = 'next-ui-action-silent'
+export const MARK_ACTION_CHANGE_HANDLED = 'mark-action-change-handled'
+export const ALERT = 'alert'
+export const ALERT_HANDLED = 'alert-handled'
diff --git a/src/components/farm/actions.js b/src/components/farm/actions.js
new file mode 100644
index 0000000..1921671
--- /dev/null
+++ b/src/components/farm/actions.js
@@ -0,0 +1,115 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
+ SPACE_PUSH_PLAYER, SPACE_CLEAR_PLAYERS, SET_OLD_MESSAGES, MESSAGE_PANEL_SPACE,
+ MP_MOUSE, SET_MP_DIMS, MARK_ACTION_CHANGE_HANDLED, SET_NEXT_ACTION,
+ MOVE_PLAYER, NEXT_UI_ACTION, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED
+ } from './actionTypes.js'
+
+export { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
+ spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace,
+ mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction,
+ markActionChangeHandled, nextUIActionSilent, alert, alertHandled }
+
+function updateGame(update) {
+ return { type: UPDATE_GAME,
+ update };
+}
+
+function updatePlayer(update) {
+ return { type: UPDATE_PLAYER,
+ update };
+}
+
+function gameState(state) {
+ return { type: GAME_STATE,
+ state };
+}
+
+function setSelectedCard(card) {
+ return { type: SET_SELECTED_CARD,
+ // TODO share with initialState ui.card
+ card: card ? card : { type: 'no-card', contents: '', total: 0 } }
+}
+
+function setCards(cards) {
+ return { type: SET_CARDS,
+ cards };
+}
+
+function spacePushPlayer(id, player) {
+ return { type: SPACE_PUSH_PLAYER,
+ id,
+ player };
+}
+
+function spaceClearPlayers(id) {
+ return { type: SPACE_CLEAR_PLAYERS,
+ id };
+}
+
+function setOldMessages(messages) {
+ return { type: SET_OLD_MESSAGES,
+ messages };
+}
+
+function setMessagePanelSpace(space) {
+ return { type: MESSAGE_PANEL_SPACE,
+ space };
+}
+
+function mpMouse(mouseX, mouseY) {
+ return { type: MP_MOUSE,
+ mouseX, mouseY };
+}
+
+function setMPDims(minWidth, minHeight, maxWidth, maxHeight) {
+ return { type: SET_MP_DIMS,
+ minWidth, minHeight, maxWidth, maxHeight };
+}
+
+function movePlayer(newSpace, oldSpace, player) {
+ return { type: MOVE_PLAYER,
+ newSpace, oldSpace, player };
+}
+
+function nextUIAction() {
+ return { type: NEXT_UI_ACTION };
+}
+
+function nextUIActionSilent() {
+ return { type: NEXT_UI_ACTION_SILENT };
+}
+
+function setNextAction(action, value) {
+ return { type: SET_NEXT_ACTION,
+ action, value };
+}
+
+function markActionChangeHandled() {
+ return { type: MARK_ACTION_CHANGE_HANDLED };
+}
+
+function alert(value) {
+ return { type: ALERT, value };
+}
+
+function alertHandled() {
+ return { type: ALERT_HANDLED };
+}
diff --git a/src/components/farm/interface.js b/src/components/farm/interface.js
new file mode 100644
index 0000000..3737603
--- /dev/null
+++ b/src/components/farm/interface.js
@@ -0,0 +1,177 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import { GAME_STATES, ALERTS } from '../../constants.js'
+
+import { batch } from 'react-redux'
+import * as websocket from '../../websocket.js'
+
+import { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
+ movePlayer, setOldMessages, markActionChangeHandled,
+ mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert
+ } from './actions.js'
+
+export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
+ submitTradeDeny, submitTradeCancel, audit, handleMessage,
+ nextAction, buyUncleBert, actionsFinished }
+
+let store;
+
+let spacesWithPlayers = [];
+let loop = 0;
+function handleMessage(evt) {
+ const data = JSON.parse(evt.data),
+ type = data.event;
+
+ if (data.event === 'error') {
+ console.log('error:' + data.exn);
+ return;
+ }
+ batch(() => {
+ if (data.player.state === GAME_STATES.preTurn &&
+ data.game.otherPlayers.length > 0 &&
+ store.getState().farm.player.state !== GAME_STATES.preTurn) {
+ store.dispatch(alert(ALERTS.beginTurn));
+ } else if (data.game.otherPlayers.length > 0 &&
+ data.game.currentPlayer !== store.getState().farm.game.currentPlayer) {
+ store.dispatch(alert(ALERTS.otherPlayersTurn));
+ }
+ store.dispatch(updatePlayer(data.player));
+ if (data.event === 'init') {
+ store.dispatch(movePlayer(data.player.space, 0, data.player.color));
+ }
+ // new player(s) added to game, put them on the board
+ if (data.game.otherPlayers.length !== store.getState().farm.game.otherPlayers.length) {
+ const otherPlayers = store.getState().farm.game.otherPlayers;
+ const newPlayers = data.game.otherPlayers.filter(
+ x => !otherPlayers.find(y => y.player.name === x.player.name));
+ for (const p of newPlayers) {
+ store.dispatch(movePlayer(p.player.space, 0, p.player.color));
+ }
+ }
+ const oldMessages = store.getState().farm.game.messages.slice(0, 20);
+ store.dispatch(updateGame(data.game));
+ store.dispatch(setOldMessages(oldMessages));
+ if (data.player.cards.length > 0) {
+ store.dispatch(setSelectedCard(data.player.cards[0]));
+ } else {
+ store.dispatch(setSelectedCard());
+ }
+ store.dispatch(setCards(data.player.cards));
+ if (data.event === 'action') {
+ if (data.player.name !== data.game.currentPlayer &&
+ data.action !== 'roll') {
+ store.dispatch(nextUIAction());
+ }
+ store.dispatch(setNextAction(data.action, data.value));
+ if (data.action === 'roll') {
+ store.dispatch(nextUIAction());
+ }
+ }
+ if (data.player.state === GAME_STATES.midTurn &&
+ data.player.cash < 0 &&
+ !store.getState().farm.ui.nextAction) {
+ store.dispatch(alert(ALERTS.raiseMoney));
+ }
+ });
+};
+
+let sendCommand;
+
+function buy(id, cash) {
+ sendCommand({ type: 'buy', id: id, cash: cash });
+}
+
+function roll() {
+ store.dispatch(setNextAction('pre-rolling', false));
+ store.dispatch(nextUIActionSilent());
+ sendCommand({ type: 'roll' });
+}
+
+function endTurn() {
+ store.dispatch(gameState(GAME_STATES.turnEnded));
+ sendCommand({ type: 'turn-ended' });
+}
+
+function loan(amount) {
+ sendCommand({ type: 'loan', amount: amount });
+}
+
+function trade(parameters) {
+ sendCommand({ type: 'trade',
+ parameters: parameters });
+}
+
+function submitTradeAccept() {
+ sendCommand({ type: 'trade-accept' });
+}
+
+function submitTradeDeny() {
+ sendCommand({ type: 'trade-deny' });
+}
+
+function submitTradeCancel() {
+ sendCommand({ type: 'trade-cancel' });
+}
+
+function audit() {
+ sendCommand({ type: 'audit' });
+}
+
+function nextAction() {
+ sendCommand({ type: 'next-action' });
+}
+
+function buyUncleBert() {
+ sendCommand({ type: 'buy-uncle-bert' });
+}
+
+function actionsFinished() {
+ sendCommand({ type: 'actions-finished' });
+}
+
+function initialize(st, sc) {
+ store = st;
+ sendCommand = sc;
+
+ const unsubscribe = store.subscribe(
+ () => {
+ const state = store.getState();
+ if (state.farm.player.name === state.farm.game.currentPlayer
+ && !state.farm.ui.actionChangeHandled) {
+ store.dispatch(markActionChangeHandled());
+ nextAction();
+ }
+ });
+
+ // mpDims.mouseX = e.clientX
+ // window.onmousemove = e => store.
+ // dispatch(mpMouse(e.clientX, store.getState().farm.mpDims.mouseY));
+ // document.addEventListener('keydown', keydown);
+}
+
+function keydown(e) {
+ switch (e.key) {
+ case 'r':
+ roll();
+ break;
+ case 'e':
+ endTurn();
+ break;
+ }
+}
diff --git a/src/components/farm/reducers.js b/src/components/farm/reducers.js
new file mode 100644
index 0000000..2baf4e6
--- /dev/null
+++ b/src/components/farm/reducers.js
@@ -0,0 +1,182 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
+ SPACE_PUSH_PLAYER, SPACE_CLEAR_PLAYERS,
+ SET_OLD_MESSAGES, MESSAGE_PANEL_SPACE, MP_MOUSE,
+ SET_MP_DIMS, MOVE_PLAYER, SET_NEXT_ACTION, NEXT_UI_ACTION,
+ MARK_ACTION_CHANGE_HANDLED, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED
+ } from './actionTypes.js'
+import { GAME_STATES } from '../../constants.js'
+import { spaceContent, corners } from 'game.js'
+
+const spaces =
+ [[corners[0], 'buy'],
+ ['January', 'buy'],
+ ['January', 'buy'],
+ ['January', 'buy'],
+ ['January', 'buy'],
+ ['February','buy'],
+ ['February', 'buy'],
+ ['February', 'buy'],
+ ['February', 'buy'],
+ ['March', 'buy'],
+ ['March', 'buy'],
+ ['March', 'buy'],
+ ['March', 'buy'],
+ ['April', 'buy'],
+ [corners[1], 'buy'],
+ ['April', 'none'],
+ ['April', 'none'],
+ ['May', 'none'],
+ ['May', 'none'],
+ ['May', 'hay'],
+ ['May', 'hay'],
+ ['June', 'hay'],
+ ['June', 'hay'],
+ ['June', 'cherry'],
+ ['June', 'cherry'],
+ [corners[2], 'cherry'],
+ ['July', 'hay'],
+ ['July', 'hay'],
+ ['July', 'hay'],
+ ['July', 'wheat'],
+ ['August', 'wheat'],
+ ['August', 'wheat'],
+ ['August', 'wheat'],
+ ['August', 'wheat'],
+ ['September', 'hay'],
+ ['September', 'hay'],
+ ['September', 'cows'],
+ [corners[3], 'cows'],
+ ['September', 'cows'],
+ ['October', 'cows'],
+ ['October', 'hay'],
+ ['October', 'hay'],
+ ['October', 'apple'],
+ ['November', 'apple'],
+ ['November', 'apple'],
+ ['November', 'apple'],
+ ['November', 'corn'],
+ ['December', 'corn'],
+ ['December', 'corn']]
+ .map((s, i) => {
+ return { month: s[0], description: spaceContent[i],
+ type: s[1], key: i, players: [] }});
+
+const initialState = {
+ player: { cash: 5000,
+ lastCash: 5000,
+ debt: 5000,
+ spaces,
+ state: GAME_STATES.turnEnded,
+ assets: { hay: 10, grain: 10, fruit: 0, cows: 0, harvester: 0, tractor: 0 },
+ color: '',
+ name: '',
+ ridges: { ridge1: 0, ridge2: 0, ridge3: 0, ridge4: 0 },
+ space: 0,
+ trade: {}
+ },
+ game: { auditThreshold: 250000,
+ calledAudit: false,
+ currentPlayer: '',
+ messages: [],
+ otherPlayers: [],
+ state: GAME_STATES.preTurn,
+ turn: 0,
+ oldMessages: [] },
+ ui: { card: { type: 'no-card', contents: '', total: 0 },
+ cards: [],
+ action: false,
+ actionValue: null,
+ nextAction: false,
+ nextActionValue: null,
+ actionChangeHandled: true,
+ alert: false,
+ alertHandled: false },
+ spaces: spaces,
+ space: null,
+ // message panel dimenions
+ mpDims: { mouseX: 0, mouseY: 0,
+ minWidth: 0, minHeight: 0, maxWidth: 0, maxHeight: 0,
+ padding: 8 },
+ profile: false,
+ profileTurns: 500
+}
+
+export default function(state = initialState, action) {
+ switch (action.type) {
+ case UPDATE_GAME:
+ return { ...state, game: { ...state.game, ...action.update }};
+ case UPDATE_PLAYER:
+ return { ...state, player: action.update };
+ case GAME_STATE:
+ return { ...state, game: { ...state.game, state: action.state } };
+ case SET_SELECTED_CARD:
+ return { ...state, ui: { ...state.ui, card: action.card }};
+ case SET_CARDS:
+ return { ...state, ui: { ...state.ui, cards: action.cards }};
+ case MOVE_PLAYER:
+ return { ...state, spaces: state.spaces
+ .map((item, index) => {
+ if (index === action.newSpace &&
+ item.players.indexOf(action.player) === -1) {
+ return { ...item, players: [...item.players, action.player]};
+ } else if (index === action.oldSpace) {
+ return { ...item,
+ players: item.players
+ .filter(x => x !== action.player) };
+ }
+ return item;
+ })
+ };
+ case SET_OLD_MESSAGES:
+ return { ...state, oldMessages: action.messages };
+ case MESSAGE_PANEL_SPACE:
+ return { ...state, space: action.space };
+ case MP_MOUSE:
+ return { ...state, mpDims: { ...state.mpDims,
+ mouseX: action.mouseX, mouseY: action.mouseY }};
+ case SET_MP_DIMS:
+ return { ...state, mpDims: { ...state.mpDims,
+ minWidth: action.minWidth,
+ minHeight: action.minHeight,
+ maxWidth: action.maxWidth,
+ maxHeight: action.maxHeight }};
+ case SET_NEXT_ACTION:
+ return { ...state, ui: { ...state.ui, nextAction: action.action,
+ nextActionValue: action.value }};
+ case NEXT_UI_ACTION:
+ return { ...state, ui: { ...state.ui, action: state.ui.nextAction,
+ actionValue: state.ui.nextActionValue,
+ actionChangeHandled: !state.ui.nextAction }};
+ case NEXT_UI_ACTION_SILENT: // don't set actionChangeHandled
+ return { ...state, ui: { ...state.ui, action: state.ui.nextAction,
+ actionValue: state.ui.nextActionValue }};
+ case MARK_ACTION_CHANGE_HANDLED:
+ return { ...state, ui: { ...state.ui, actionChangeHandled: true }};
+ case ALERT:
+ return { ...state, ui: { ...state.ui,
+ alert: action.value,
+ alertHandled: action.value === false ? true : false }};
+ case ALERT_HANDLED:
+ return { ...state, ui: { ...state.ui, alertHandled: true }};
+ default:
+ return state;
+ }
+}
diff --git a/src/components/join-game/JoinGame.jsx b/src/components/join-game/JoinGame.jsx
new file mode 100644
index 0000000..d9abfba
--- /dev/null
+++ b/src/components/join-game/JoinGame.jsx
@@ -0,0 +1,97 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React, { Fragment } from 'react'
+import { connect } from 'react-redux'
+
+import { GroupBox, Row, Col, Button } from '../widgets.jsx'
+
+import { startOrJoinGame } from '../start/actions.js'
+
+import NewGame from '../new-game/NewGame.jsx'
+
+const JoinGameScreens = { list: 'list', details: 'details' };
+
+class JoinGame extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ screen: JoinGameScreens.list,
+ game: null
+ };
+ }
+
+ handleClickGame = game => {
+ this.setState({ screen: JoinGameScreens.details,
+ game: game
+ });
+ }
+
+ handleBack = e => {
+ this.setState({ screen: JoinGameScreens.list });
+ }
+
+ handleJoinAsExisting = e => {
+ this.props.startOrJoinGame({ type: 'join-as-existing',
+ playerName: e.target.text,
+ gameName: this.state.game.name });
+ }
+
+ render() {
+ return (
+ <GroupBox title='Join Game'>
+ <Row>
+ <Col width='12'>
+ {this.state.screen === JoinGameScreens.list ?
+ (<ul>
+ {this.props.games
+ .map((g, i) =>
+ (<li key={i}>
+ <a href='#' onClick={() => this.handleClickGame(g)}>{g.name}</a>
+ </li>))}
+ </ul>)
+ : (<Fragment>
+ <p><a href="#" onClick={this.handleBack}>back to games</a></p>
+ <h3><b>Game:</b> {this.state.game.name}</h3>
+ <h4>Join as existing player:</h4>
+ <ul>
+ {this.state.game.players.map((p, i) =>
+ (<li key={i}>
+ <a href='#' onClick={this.handleJoinAsExisting}>
+ {p}
+ </a>
+ </li>))}
+ </ul>
+ <NewGame colors={this.state.game.colors}
+ button={'Join'}
+ showGameName={false}
+ gameName={this.state.game.name}
+ type={'join-game'}
+ title={'Join as New Player'} />
+ </Fragment>)}
+ </Col>
+ </Row>
+ </GroupBox>
+ );
+ }
+}
+
+export default connect(
+ state => state.start.start,
+ { startOrJoinGame }
+)(JoinGame)
diff --git a/src/components/new-game/NewGame.jsx b/src/components/new-game/NewGame.jsx
new file mode 100644
index 0000000..0dc72be
--- /dev/null
+++ b/src/components/new-game/NewGame.jsx
@@ -0,0 +1,104 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React, { Fragment } from 'react'
+import { connect } from 'react-redux'
+
+import { GroupBox, Row, Col, Button } from '../widgets.jsx'
+
+import { startOrJoinGame } from '../start/actions.js'
+
+class NewGame extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ playerName: '',
+ checkedColor: props.colors[0],
+ gameName: props.gameName || ''
+ };
+ }
+
+ handleInputChange = e => {
+ const target = e.target,
+ value = target.type === 'checkbox' ? target.name : target.value,
+ name = target.type === 'checkbox' ? 'checkedColor' : target.name;
+
+ this.setState({
+ [name]: value
+ });
+ }
+
+ handleSubmit = e => {
+ e.preventDefault();
+ this.props.startOrJoinGame(Object.assign({ type: this.props.type }, this.state));
+ }
+
+ render() {
+ let playerNameInput;
+ return (
+ <GroupBox title={this.props.title}>
+ <form onSubmit={this.handleSubmit}>
+ <Row>
+ <Col width='12'>
+ <label>Your Name
+ <input type='text' name='playerName'
+ value={this.state.playerName}
+ onChange={this.handleInputChange} />
+ </label>
+ </Col>
+ </Row>
+ <Row>
+ <Col width='12'>
+ <label>Your Color</label>
+ {this.props.colors
+ .map(c =>
+ (<label key={c} className={'player player-selectable player-' + c + (this.state.checkedColor === c ? ' player-selected' : '')}>
+ <input type='checkbox'
+ checked={this.state.checkedColor === c}
+ onChange={this.handleInputChange}
+ name={c} />
+ </label>))
+ }
+ <br /><br />
+ </Col>
+ </Row>
+ {this.props.showGameName && (
+ <Row>
+ <Col width='12'>
+ <label>Game Name
+ <input type='text' name='gameName' value={this.state.gameName}
+ onChange={this.handleInputChange} />
+ </label>
+ </Col>
+ </Row>
+ )}
+ <Row>
+ <Col width='12'>
+ <Button type='submit'>{this.props.button} Game</Button>
+ </Col>
+ </Row>
+ </form>
+ </GroupBox>
+ );
+ }
+}
+
+export default connect(
+ null,
+ { startOrJoinGame }
+)(NewGame)
diff --git a/src/components/start/Start.jsx b/src/components/start/Start.jsx
new file mode 100644
index 0000000..2966516
--- /dev/null
+++ b/src/components/start/Start.jsx
@@ -0,0 +1,94 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React, { Fragment } from 'react'
+import { connect } from 'react-redux'
+
+import { GroupBox, Row, Col } from '../widgets.jsx'
+import { startOrJoinGame } from './actions.js'
+
+const JoinGameScreens = { list: 'list', details: 'details' };
+
+class JoinGameComp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ screen: JoinGameScreens.list,
+ game: null
+ };
+ }
+
+ handleClickGame = game => {
+ this.setState({ screen: JoinGameScreens.details,
+ game: game
+ });
+ }
+
+ handleBack = e => {
+ this.setState({ screen: JoinGameScreens.list });
+ }
+
+ handleJoinAsExisting = e => {
+ this.props.startOrJoinGame({ type: 'join-as-existing',
+ playerName: e.target.text,
+ gameName: this.state.game.name });
+ }
+
+ render() {
+ return (
+ <GroupBox title='Join Game'>
+ <Row>
+ <Col width='12'>
+ {this.state.screen === JoinGameScreens.list ?
+ (<ul>
+ {this.props.games
+ .map((g, i) =>
+ (<li key={i}>
+ <a href='#' onClick={() => this.handleClickGame(g)}>{g.name}</a>
+ </li>))}
+ </ul>)
+ : (<Fragment>
+ <p><a href="#" onClick={this.handleBack}>back to games</a></p>
+ <h3><b>Game:</b> {this.state.game.name}</h3>
+ <h4>Join as existing player:</h4>
+ <ul>
+ {this.state.game.players.map((p, i) =>
+ (<li key={i}>
+ <a href='#' onClick={this.handleJoinAsExisting}>
+ {p}
+ </a>
+ </li>))}
+ </ul>
+ <NewGame colors={this.state.game.colors}
+ button={'Join'}
+ showGameName={false}
+ gameName={this.state.game.name}
+ type={'join-game'}
+ title={'Join as New Player'} />
+ </Fragment>)}
+ </Col>
+ </Row>
+ </GroupBox>
+ );
+ }
+}
+
+const JoinGame = connect(
+ null,
+ { startOrJoinGame }
+)(JoinGameComp)
diff --git a/src/components/start/actionTypes.js b/src/components/start/actionTypes.js
new file mode 100644
index 0000000..4a7e716
--- /dev/null
+++ b/src/components/start/actionTypes.js
@@ -0,0 +1,20 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+export const SET_START_GAMES = 'set-start-games';
+export const START_OR_JOIN_GAME = 'start-or-join-game';
diff --git a/src/components/start/actions.js b/src/components/start/actions.js
new file mode 100644
index 0000000..a2b64ac
--- /dev/null
+++ b/src/components/start/actions.js
@@ -0,0 +1,31 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import { SET_START_GAMES, START_OR_JOIN_GAME } from './actionTypes.js'
+
+export { setStartGames, startOrJoinGame }
+
+function setStartGames(games) {
+ return { type: SET_START_GAMES,
+ games };
+}
+
+function startOrJoinGame(msg) {
+ return { type: START_OR_JOIN_GAME,
+ msg };
+}
diff --git a/src/components/start/reducers.js b/src/components/start/reducers.js
new file mode 100644
index 0000000..86c9fc9
--- /dev/null
+++ b/src/components/start/reducers.js
@@ -0,0 +1,37 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import { SET_START_GAMES, START_OR_JOIN_GAME } from './actionTypes.js'
+import { SCREENS } from '../../constants.js'
+
+const initialState = {
+ start: { games: [] },
+ msg: null
+};
+
+export default function(state = initialState, action) {
+ switch (action.type) {
+ case SET_START_GAMES:
+ return { ...state, start: { ...state.start, games: action.games }};
+ case START_OR_JOIN_GAME:
+ return { ...state, msg: action.msg };
+ default:
+ return state;
+ }
+}
+
diff --git a/src/components/tractor/Tractor.jsx b/src/components/tractor/Tractor.jsx
new file mode 100644
index 0000000..aa7c384
--- /dev/null
+++ b/src/components/tractor/Tractor.jsx
@@ -0,0 +1,40 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import TractorImg from './../../../assets/img/tractor-offset.svg'
+import TractorAndSpikesImg from './../../../assets/img/tractor-offset-and-spikes.svg'
+import TractorSpikesImg from './../../../assets/img/tractor-spikes-offset.svg'
+
+import React, { Fragment } from 'react'
+
+export default class Tractor extends React.Component {
+ render() {
+ return (
+ <Fragment>
+ <div className={'tractor ' + (this.props.className ? this.props.className : '')}>
+ <img src={this.props.spikes ? TractorImg : TractorAndSpikesImg} />
+ </div>
+ {this.props.spikes ? (
+ <div className='tractor spikes'>
+ <img src={TractorSpikesImg} />
+ </div>
+ ) : (<Fragment />)}
+ </Fragment>
+ );
+ }
+}
diff --git a/src/components/welcome/Welcome.jsx b/src/components/welcome/Welcome.jsx
new file mode 100644
index 0000000..c047a8a
--- /dev/null
+++ b/src/components/welcome/Welcome.jsx
@@ -0,0 +1,46 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React, { Fragment } from 'react'
+import { connect } from 'react-redux'
+
+import { GroupBox, Row, Col, Button } from '../widgets.jsx'
+import { start } from '../app/actions.js'
+
+
+class Welcome extends React.Component {
+ render() {
+ return (
+ <Fragment>
+ <div className='intro-text'>
+ <div className='game-card'>
+ Your ancestors were farmers on one of the first transports to Alpha Centuari{`'`}s Proxima b. The growing season is short and harsh but the colonists depend on you for their food. Are you up to the challenge?
+ </div>
+ </div>
+ <Button size='large' className='shadow intro' onClick={this.props.start}>
+ Begin
+ </Button>
+ </Fragment>
+ );
+ }
+}
+
+export default connect(
+ state => state,
+ { start }
+)(Welcome)
diff --git a/src/components/widgets.jsx b/src/components/widgets.jsx
new file mode 100644
index 0000000..2bf6007
--- /dev/null
+++ b/src/components/widgets.jsx
@@ -0,0 +1,69 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+import React, { Fragment } from 'react'
+
+export { GroupBox, Row, Col, Button }
+
+class GroupBox extends React.Component {
+ render() {
+ return (
+ <div className='panel card'>
+ {this.props.title ?
+ (<div className='card-divider'>
+ {this.props.title}
+ </div>) : (<Fragment />)}
+ <div className={'card-section ' + this.props.className ? this.props.className : ''}>
+ {this.props.children}
+ </div>
+ </div>
+ );
+ }
+}
+
+class Row extends React.Component {
+ render() {
+ return (<div className={'grid-x full-width ' +
+ (this.props.collapse ? 'collapse' : '') + ' ' +
+ (this.props.className ? this.props.className : '')} >
+ {this.props.children}</div>);
+ }
+}
+
+class Col extends React.Component {
+ render() {
+ return (
+ <div className={'cell small-' + this.props.width}>
+ {this.props.children}
+ </div>
+ );
+ }
+}
+
+class Button extends React.Component {
+ render() {
+ return (
+ <button className={'button ' + (this.props.size ? this.props.size : '') +
+ ' ' + (this.props.className ? this.props.className : '')}
+ type={this.props.type || 'button'}
+ onClick={this.props.onClick} >
+ {this.props.children}
+ </button>
+ );
+ }
+}
diff --git a/src/constants.js b/src/constants.js
new file mode 100644
index 0000000..4cea1a6
--- /dev/null
+++ b/src/constants.js
@@ -0,0 +1,34 @@
+// Copyright 2020 Thomas Hintz
+//
+// This file is part of the Alpha Centauri Farming project.
+//
+// The Alpha Centauri Farming project is free software: you can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License as published by the Free Software Foundation, either
+// version 3 of the License, or (at your option) any later version.
+//
+// The Alpha Centauri Farming project is distributed in the hope that
+// it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with the Alpha Centauri Farming project. If not, see
+// <https://www.gnu.org/licenses/>.
+
+export const SCREENS = {
+ intro: 'intro',
+ start: 'start',
+ play: 'play',
+ newGame: 'new-game',
+ joinGame: 'join-game'
+};
+
+export const GAME_STATES = { preTurn: 'pre-turn',
+ midTurn: 'mid-turn',
+ turnEnded: 'turn-ended' };
+export const rootId = 'initial-element';
+export const messagePanelId = 'message-panel';
+export const ALERTS = { beginTurn: 'begin-turn',
+ otherPlayersTurn: 'other-players-turn',
+ raiseMoney: 'raise-money' }
diff --git a/src/foundation/_global.scss b/src/foundation/_global.scss
new file mode 100644
index 0000000..3210c8c
--- /dev/null
+++ b/src/foundation/_global.scss
@@ -0,0 +1,244 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+// sass-lint:disable no-color-literals, no-qualifying-elements
+
+////
+/// @group global
+////
+
+@import 'util/util';
+
+/// Font size attribute applied to `<html>` and `<body>`. We use 100% by default so the value is inherited from the user's browser settings.
+/// @type Number
+$global-font-size: 100% !default;
+
+/// Global width of your site. Used by the grid to determine row width.
+/// @type Number
+$global-width: rem-calc(1200) !default;
+
+/// Default line height for all type. `$global-lineheight` is 24px while `$global-font-size` is 16px
+/// @type Number
+$global-lineheight: 1.5 !default;
+
+/// Colors used for buttons, callouts, links, etc. There must always be a color called `primary`.
+/// @type Map
+$foundation-palette: (
+ primary: #1779ba,
+ secondary: #767676,
+ success: #3adb76,
+ warning: #ffae00,
+ alert: #cc4b37,
+) !default;
+
+/// Color used for light gray UI items.
+/// @type Color
+$light-gray: #e6e6e6 !default;
+
+/// Color used for medium gray UI items.
+/// @type Color
+$medium-gray: #cacaca !default;
+
+/// Color used for dark gray UI items.
+/// @type Color
+$dark-gray: #8a8a8a !default;
+
+/// Color used for black ui items.
+/// @type Color
+$black: #0a0a0a !default;
+
+/// Color used for white ui items.
+/// @type Color
+$white: #fefefe !default;
+
+/// Background color of the body.
+/// @type Color
+$body-background: $white !default;
+
+/// Text color of the body.
+/// @type Color
+$body-font-color: $black !default;
+
+/// Font stack of the body.
+/// @type List
+$body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif !default;
+
+/// Set to `true` to enable antialiased type, using the `-webkit-font-smoothing` and `-moz-osx-font-smoothing` CSS properties.
+/// @type Boolean
+$body-antialiased: true !default;
+
+/// Global value used for margin on components.
+/// @type Number
+$global-margin: 1rem !default;
+
+/// Global value used for padding on components.
+/// @type Number
+$global-padding: 1rem !default;
+
+/// Global value used for positioning on components.
+/// @type Number
+$global-position: 1rem !default;
+
+/// Global font weight used for normal type.
+/// @type Keyword | Number
+$global-weight-normal: normal !default;
+
+/// Global font weight used for bold type.
+/// @type Keyword | Number
+$global-weight-bold: bold !default;
+
+/// Global value used for all elements that have a border radius.
+/// @type Number
+$global-radius: 0 !default;
+
+/// Global value used for all menu styles. Can be overwritten at individual menu component level.
+/// @type Number
+$global-menu-padding: 0.7rem 1rem !default;
+
+/// Global value used for all menu styles. Nested margin for submenu.
+$global-menu-nested-margin: 1rem !default;
+
+/// Sets the text direction of the CSS. Can be either `ltr` or `rtl`.
+/// @type Keyword
+$global-text-direction: ltr !default;
+
+/// Enables flexbox for components that support it.
+/// @type Boolean
+$global-flexbox: true !default;
+
+/// Enabled responsive breakpoints for prototypes if applicable
+/// @type Boolean
+$global-prototype-breakpoints: false !default;
+
+/// Button cursor's value, `auto` by default
+/// @type Keyword
+$global-button-cursor: auto !default;
+
+@if not map-has-key($foundation-palette, primary) {
+ @error 'In $foundation-palette, you must have a color named "primary".';
+}
+
+// Internal variables used for text direction
+$global-left: if($global-text-direction == rtl, right, left);
+$global-right: if($global-text-direction == rtl, left, right);
+
+// Internal variable that contains the flex justifying options
+$-zf-flex-justify: -zf-flex-justify($global-text-direction);
+
+/// Global tolerance for color pick contrast.
+/// @type Number
+$global-color-pick-contrast-tolerance: 0 !default;
+
+// Internal variables used for colors
+@include add-foundation-colors;
+
+@mixin foundation-global-styles {
+ @include foundation-normalize;
+
+ // These styles are applied to a <meta> tag, which is read by the Foundation JavaScript
+ .foundation-mq {
+ font-family: '#{-zf-bp-serialize($breakpoints)}';
+ }
+
+ html {
+ box-sizing: border-box;
+ font-size: $global-font-size;
+ }
+
+ // Set box-sizing globally to handle padding and border widths
+ *,
+ *::before,
+ *::after {
+ box-sizing: inherit;
+ }
+
+ // Default body styles
+ body {
+ margin: 0;
+ padding: 0;
+
+ background: $body-background;
+
+ font-family: $body-font-family;
+ font-weight: $global-weight-normal;
+ line-height: $global-lineheight;
+ color: $body-font-color;
+
+ @if ($body-antialiased) {
+ -webkit-font-smoothing: antialiased; // sass-lint:disable-line no-vendor-prefixes
+ -moz-osx-font-smoothing: grayscale; // sass-lint:disable-line no-vendor-prefixes
+ }
+ }
+
+ img {
+ // Get rid of gap under images by making them display: inline-block; by default
+ display: inline-block;
+ vertical-align: middle;
+
+ // Grid defaults to get images and embeds to work properly
+ max-width: 100%;
+ height: auto;
+ -ms-interpolation-mode: bicubic;
+ }
+
+ // Make sure textarea takes on height automatically
+ textarea {
+ height: auto;
+ min-height: 50px;
+ border-radius: $global-radius;
+ }
+
+ // Make select elements are 100% width by default
+ select {
+ box-sizing: border-box;
+ width: 100%;
+ border-radius: $global-radius;
+ }
+
+ // Styles Google Maps and MapQuest embeds properly
+ // sass-lint:disable-line no-ids
+ .map_canvas,
+ .mqa-display {
+ img,
+ embed,
+ object {
+ max-width: none !important;
+ }
+ }
+
+ // Reset <button> styles created by most browsers
+ button {
+ @include disable-mouse-outline;
+ padding: 0;
+ appearance: none;
+ border: 0;
+ border-radius: $global-radius;
+ background: transparent;
+ line-height: 1;
+ cursor: $global-button-cursor;
+ }
+
+ // Prevent text overflow on pre
+ pre {
+ overflow: auto;
+ }
+
+ // Make reset inherit font-family instead of settings sans-serif
+ button,
+ input,
+ optgroup,
+ select,
+ textarea {
+ font-family: inherit;
+ }
+
+ // Internal classes to show/hide elements in JavaScript
+ .is-visible {
+ display: block !important;
+ }
+
+ .is-hidden {
+ display: none !important;
+ }
+}
diff --git a/src/foundation/_settings.scss b/src/foundation/_settings.scss
new file mode 100644
index 0000000..8b59e9e
--- /dev/null
+++ b/src/foundation/_settings.scss
@@ -0,0 +1,896 @@
+// Foundation for Sites Settings
+// -----------------------------
+//
+// Table of Contents:
+//
+// 1. Global
+// 2. Breakpoints
+// 3. The Grid
+// 4. Base Typography
+// 5. Typography Helpers
+// 6. Abide
+// 7. Accordion
+// 8. Accordion Menu
+// 9. Badge
+// 10. Breadcrumbs
+// 11. Button
+// 12. Button Group
+// 13. Callout
+// 14. Card
+// 15. Close Button
+// 16. Drilldown
+// 17. Dropdown
+// 18. Dropdown Menu
+// 19. Flexbox Utilities
+// 20. Forms
+// 21. Label
+// 22. Media Object
+// 23. Menu
+// 24. Meter
+// 25. Off-canvas
+// 26. Orbit
+// 27. Pagination
+// 28. Progress Bar
+// 29. Prototype Arrow
+// 30. Prototype Border-Box
+// 31. Prototype Border-None
+// 32. Prototype Bordered
+// 33. Prototype Display
+// 34. Prototype Font-Styling
+// 35. Prototype List-Style-Type
+// 36. Prototype Overflow
+// 37. Prototype Position
+// 38. Prototype Rounded
+// 39. Prototype Separator
+// 40. Prototype Shadow
+// 41. Prototype Sizing
+// 42. Prototype Spacing
+// 43. Prototype Text-Decoration
+// 44. Prototype Text-Transformation
+// 45. Prototype Text-Utilities
+// 46. Responsive Embed
+// 47. Reveal
+// 48. Slider
+// 49. Switch
+// 50. Table
+// 51. Tabs
+// 52. Thumbnail
+// 53. Title Bar
+// 54. Tooltip
+// 55. Top Bar
+// 56. Xy Grid
+
+@import 'util/util';
+
+// 1. Global
+// ---------
+
+$global-font-size: 100%;
+$global-width: rem-calc(1200);
+$global-lineheight: 1.5;
+$foundation-palette: (
+ primary: #B34534, // #1779ba
+ secondary: #BF7B71, // #767676
+ success: #3adb76,
+ warning: #ffae00,
+ alert: #cc4b37,
+);
+// other colors: #FF624A, #FFA496, #7F3125
+$light-gray: #e6e6e6;
+$medium-gray: #cacaca;
+$dark-gray: #8a8a8a;
+$black: #0a0a0a;
+$white: #fefefe;
+$body-background: $white;
+$body-font-color: $black;
+$body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;
+$body-antialiased: true;
+$global-margin: 1rem;
+$global-padding: 1rem;
+$global-position: 1rem;
+$global-weight-normal: normal;
+$global-weight-bold: bold;
+$global-radius: 0;
+$global-menu-padding: 0.7rem 1rem;
+$global-menu-nested-margin: 1rem;
+$global-text-direction: ltr;
+$global-flexbox: true;
+$global-prototype-breakpoints: false;
+$global-button-cursor: auto;
+$global-color-pick-contrast-tolerance: 0;
+$print-transparent-backgrounds: true;
+$print-hrefs: true;
+
+@include add-foundation-colors;
+
+// 2. Breakpoints
+// --------------
+
+$breakpoints: (
+ small: 0,
+ medium: 640px,
+ large: 1024px,
+ xlarge: 1200px,
+ xxlarge: 1440px,
+);
+$breakpoints-hidpi: (
+ hidpi-1: 1,
+ hidpi-1-5: 1.5,
+ hidpi-2: 2,
+ retina: 2,
+ hidpi-3: 3
+);
+$print-breakpoint: large;
+$breakpoint-classes: (small medium large);
+
+// 3. The Grid
+// -----------
+
+$grid-row-width: $global-width;
+$grid-column-count: 12;
+$grid-column-gutter: (
+ small: 20px,
+ medium: 30px,
+);
+$grid-column-align-edge: true;
+$grid-column-alias: 'columns';
+$block-grid-max: 8;
+
+// 4. Base Typography
+// ------------------
+
+$header-font-family: $body-font-family;
+$header-font-weight: $global-weight-normal;
+$header-font-style: normal;
+$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace;
+$header-color: inherit;
+$header-lineheight: 1.4;
+$header-margin-bottom: 0.5rem;
+$header-styles: (
+ small: (
+ 'h1': ('font-size': 24),
+ 'h2': ('font-size': 20),
+ 'h3': ('font-size': 19),
+ 'h4': ('font-size': 18),
+ 'h5': ('font-size': 17),
+ 'h6': ('font-size': 16),
+ ),
+ medium: (
+ 'h1': ('font-size': 48),
+ 'h2': ('font-size': 40),
+ 'h3': ('font-size': 31),
+ 'h4': ('font-size': 25),
+ 'h5': ('font-size': 20),
+ 'h6': ('font-size': 16),
+ ),
+);
+$header-text-rendering: optimizeLegibility;
+$small-font-size: 80%;
+$header-small-font-color: $medium-gray;
+$paragraph-lineheight: 1.6;
+$paragraph-margin-bottom: 1rem;
+$paragraph-text-rendering: optimizeLegibility;
+$enable-code-inline: true;
+$anchor-color: $primary-color;
+$anchor-color-hover: scale-color($anchor-color, $lightness: -14%);
+$anchor-text-decoration: none;
+$anchor-text-decoration-hover: none;
+$hr-width: $global-width;
+$hr-border: 1px solid $medium-gray;
+$hr-margin: rem-calc(20) auto;
+$list-lineheight: $paragraph-lineheight;
+$list-margin-bottom: $paragraph-margin-bottom;
+$list-style-type: disc;
+$list-style-position: outside;
+$list-side-margin: 1.25rem;
+$list-nested-side-margin: 1.25rem;
+$defnlist-margin-bottom: 1rem;
+$defnlist-term-weight: $global-weight-bold;
+$defnlist-term-margin-bottom: 0.3rem;
+$blockquote-color: $dark-gray;
+$blockquote-padding: rem-calc(9 20 0 19);
+$blockquote-border: 1px solid $medium-gray;
+$enable-cite-block: true;
+$keystroke-font: $font-family-monospace;
+$keystroke-color: $black;
+$keystroke-background: $light-gray;
+$keystroke-padding: rem-calc(2 4 0);
+$keystroke-radius: $global-radius;
+$abbr-underline: 1px dotted $black;
+
+// 5. Typography Helpers
+// ---------------------
+
+$lead-font-size: $global-font-size * 1.25;
+$lead-lineheight: 1.6;
+$subheader-lineheight: 1.4;
+$subheader-color: $dark-gray;
+$subheader-font-weight: $global-weight-normal;
+$subheader-margin-top: 0.2rem;
+$subheader-margin-bottom: 0.5rem;
+$stat-font-size: 2.5rem;
+$cite-color: $dark-gray;
+$cite-font-size: rem-calc(13);
+$cite-pseudo-content: '\2014 \0020';
+$code-color: $black;
+$code-font-family: $font-family-monospace;
+$code-font-weight: $global-weight-normal;
+$code-background: $light-gray;
+$code-border: 1px solid $medium-gray;
+$code-padding: rem-calc(2 5 1);
+$code-block-padding: 1rem;
+$code-block-margin-bottom: 1.5rem;
+
+// 6. Abide
+// --------
+
+$abide-inputs: true;
+$abide-labels: true;
+$input-background-invalid: get-color(alert);
+$form-label-color-invalid: get-color(alert);
+$input-error-color: get-color(alert);
+$input-error-font-size: rem-calc(12);
+$input-error-font-weight: $global-weight-bold;
+
+// 7. Accordion
+// ------------
+
+$accordion-background: $white;
+$accordion-plusminus: true;
+$accordion-plus-content: '\002B';
+$accordion-minus-content: '\2013';
+$accordion-title-font-size: rem-calc(12);
+$accordion-item-color: $primary-color;
+$accordion-item-background-hover: $light-gray;
+$accordion-item-padding: 1.25rem 1rem;
+$accordion-content-background: $white;
+$accordion-content-border: 1px solid $light-gray;
+$accordion-content-color: $body-font-color;
+$accordion-content-padding: 1rem;
+
+// 8. Accordion Menu
+// -----------------
+
+$accordionmenu-padding: $global-menu-padding;
+$accordionmenu-nested-margin: $global-menu-nested-margin;
+$accordionmenu-submenu-padding: $accordionmenu-padding;
+$accordionmenu-arrows: true;
+$accordionmenu-arrow-color: $primary-color;
+$accordionmenu-item-background: null;
+$accordionmenu-border: null;
+$accordionmenu-submenu-toggle-background: null;
+$accordion-submenu-toggle-border: $accordionmenu-border;
+$accordionmenu-submenu-toggle-width: 40px;
+$accordionmenu-submenu-toggle-height: $accordionmenu-submenu-toggle-width;
+$accordionmenu-arrow-size: 6px;
+
+// 9. Badge
+// --------
+
+$badge-background: $primary-color;
+$badge-color: $white;
+$badge-color-alt: $black;
+$badge-palette: $foundation-palette;
+$badge-padding: 0.3em;
+$badge-minwidth: 2.1em;
+$badge-font-size: 0.6rem;
+
+// 10. Breadcrumbs
+// ---------------
+
+$breadcrumbs-margin: 0 0 $global-margin 0;
+$breadcrumbs-item-font-size: rem-calc(11);
+$breadcrumbs-item-color: $primary-color;
+$breadcrumbs-item-color-current: $black;
+$breadcrumbs-item-color-disabled: $medium-gray;
+$breadcrumbs-item-margin: 0.75rem;
+$breadcrumbs-item-uppercase: true;
+$breadcrumbs-item-separator: true;
+$breadcrumbs-item-separator-item: '/';
+$breadcrumbs-item-separator-item-rtl: '\\';
+$breadcrumbs-item-separator-color: $medium-gray;
+
+// 11. Button
+// ----------
+
+$button-font-family: inherit;
+$button-font-weight: null;
+$button-padding: 0.85em 1em;
+$button-margin: 0 0 $global-margin 0;
+$button-fill: solid;
+$button-background: $primary-color;
+$button-background-hover: scale-color($button-background, $lightness: -15%);
+$button-color: $white;
+$button-color-alt: $black;
+$button-radius: $global-radius;
+$button-border: 1px solid transparent;
+$button-hollow-border-width: 1px;
+$button-sizes: (
+ tiny: 0.6rem,
+ small: 0.75rem,
+ default: 0.9rem,
+ large: 1.25rem,
+);
+$button-palette: $foundation-palette;
+$button-opacity-disabled: 0.25;
+$button-background-hover-lightness: -20%;
+$button-hollow-hover-lightness: -50%;
+$button-transition: background-color 0.25s ease-out, color 0.25s ease-out;
+$button-responsive-expanded: false;
+
+// 12. Button Group
+// ----------------
+
+$buttongroup-margin: 1rem;
+$buttongroup-spacing: 1px;
+$buttongroup-child-selector: '.button';
+$buttongroup-expand-max: 6;
+$buttongroup-radius-on-each: true;
+
+// 13. Callout
+// -----------
+
+$callout-background: $white;
+$callout-background-fade: 85%;
+$callout-border: 1px solid rgba($black, 0.25);
+$callout-margin: 0 0 1rem 0;
+$callout-sizes: (
+ small: 0.5rem,
+ default: 1rem,
+ large: 3rem,
+);
+$callout-font-color: $body-font-color;
+$callout-font-color-alt: $body-background;
+$callout-radius: $global-radius;
+$callout-link-tint: 30%;
+
+// 14. Card
+// --------
+
+$card-background: $white;
+$card-font-color: $body-font-color;
+$card-divider-background: $light-gray;
+$card-border: 1px solid $light-gray;
+$card-shadow: none;
+$card-border-radius: $global-radius;
+$card-padding: $global-padding;
+$card-margin-bottom: $global-margin;
+
+// 15. Close Button
+// ----------------
+
+$closebutton-position: right top;
+$closebutton-z-index: 10;
+$closebutton-default-size: medium;
+$closebutton-offset-horizontal: (
+ small: 0.66rem,
+ medium: 1rem,
+);
+$closebutton-offset-vertical: (
+ small: 0.33em,
+ medium: 0.5rem,
+);
+$closebutton-size: (
+ small: 1.5em,
+ medium: 2em,
+);
+$closebutton-lineheight: 1;
+$closebutton-color: $dark-gray;
+$closebutton-color-hover: $black;
+
+// 16. Drilldown
+// -------------
+
+$drilldown-transition: transform 0.15s linear;
+$drilldown-arrows: true;
+$drilldown-padding: $global-menu-padding;
+$drilldown-nested-margin: 0;
+$drilldown-background: $white;
+$drilldown-submenu-padding: $drilldown-padding;
+$drilldown-submenu-background: $white;
+$drilldown-arrow-color: $primary-color;
+$drilldown-arrow-size: 6px;
+
+// 17. Dropdown
+// ------------
+
+$dropdown-padding: 1rem;
+$dropdown-background: $body-background;
+$dropdown-border: 1px solid $medium-gray;
+$dropdown-font-size: 1rem;
+$dropdown-width: 300px;
+$dropdown-radius: $global-radius;
+$dropdown-sizes: (
+ tiny: 100px,
+ small: 200px,
+ large: 400px,
+);
+
+// 18. Dropdown Menu
+// -----------------
+
+$dropdownmenu-arrows: true;
+$dropdownmenu-arrow-color: $anchor-color;
+$dropdownmenu-arrow-size: 6px;
+$dropdownmenu-arrow-padding: 1.5rem;
+$dropdownmenu-min-width: 200px;
+$dropdownmenu-background: null;
+$dropdownmenu-submenu-background: $white;
+$dropdownmenu-padding: $global-menu-padding;
+$dropdownmenu-nested-margin: 0;
+$dropdownmenu-submenu-padding: $dropdownmenu-padding;
+$dropdownmenu-border: 1px solid $medium-gray;
+$dropdown-menu-item-color-active: get-color(primary);
+$dropdown-menu-item-background-active: transparent;
+
+// 19. Flexbox Utilities
+// ---------------------
+
+$flex-source-ordering-count: 6;
+$flexbox-responsive-breakpoints: true;
+
+// 20. Forms
+// ---------
+
+$fieldset-border: 1px solid $medium-gray;
+$fieldset-padding: rem-calc(20);
+$fieldset-margin: rem-calc(18 0);
+$legend-padding: rem-calc(0 3);
+$form-spacing: rem-calc(16);
+$helptext-color: $black;
+$helptext-font-size: rem-calc(13);
+$helptext-font-style: italic;
+$input-prefix-color: $black;
+$input-prefix-background: $light-gray;
+$input-prefix-border: 1px solid $medium-gray;
+$input-prefix-padding: 1rem;
+$form-label-color: $black;
+$form-label-font-size: rem-calc(14);
+$form-label-font-weight: $global-weight-normal;
+$form-label-line-height: 1.8;
+$select-background: $white;
+$select-triangle-color: $dark-gray;
+$select-radius: $global-radius;
+$input-color: $black;
+$input-placeholder-color: $medium-gray;
+$input-font-family: inherit;
+$input-font-size: rem-calc(16);
+$input-font-weight: $global-weight-normal;
+$input-line-height: $global-lineheight;
+$input-background: $white;
+$input-background-focus: $white;
+$input-background-disabled: $light-gray;
+$input-border: 1px solid $medium-gray;
+$input-border-focus: 1px solid $dark-gray;
+$input-padding: $form-spacing / 2;
+$input-shadow: inset 0 1px 2px rgba($black, 0.1);
+$input-shadow-focus: 0 0 5px $medium-gray;
+$input-cursor-disabled: not-allowed;
+$input-transition: box-shadow 0.5s, border-color 0.25s ease-in-out;
+$input-number-spinners: true;
+$input-radius: $global-radius;
+$form-button-radius: $global-radius;
+
+// 21. Label
+// ---------
+
+$label-background: $primary-color;
+$label-color: $white;
+$label-color-alt: $black;
+$label-palette: $foundation-palette;
+$label-font-size: 0.8rem;
+$label-padding: 0.33333rem 0.5rem;
+$label-radius: $global-radius;
+
+// 22. Media Object
+// ----------------
+
+$mediaobject-margin-bottom: $global-margin;
+$mediaobject-section-padding: $global-padding;
+$mediaobject-image-width-stacked: 100%;
+
+// 23. Menu
+// --------
+
+$menu-margin: 0;
+$menu-nested-margin: $global-menu-nested-margin;
+$menu-items-padding: $global-menu-padding;
+$menu-simple-margin: 1rem;
+$menu-item-color-active: $white;
+$menu-item-color-alt-active: $black;
+$menu-item-background-active: get-color(primary);
+$menu-icon-spacing: 0.25rem;
+$menu-state-back-compat: true;
+$menu-centered-back-compat: true;
+$menu-icons-back-compat: true;
+
+// 24. Meter
+// ---------
+
+$meter-height: 1rem;
+$meter-radius: $global-radius;
+$meter-background: $medium-gray;
+$meter-fill-good: $success-color;
+$meter-fill-medium: $warning-color;
+$meter-fill-bad: $alert-color;
+
+// 25. Off-canvas
+// --------------
+
+$offcanvas-sizes: (
+ small: 250px,
+);
+$offcanvas-vertical-sizes: (
+ small: 250px,
+);
+$offcanvas-background: $light-gray;
+$offcanvas-shadow: 0 0 10px rgba($black, 0.7);
+$offcanvas-inner-shadow-size: 20px;
+$offcanvas-inner-shadow-color: rgba($black, 0.25);
+$offcanvas-overlay-zindex: 11;
+$offcanvas-push-zindex: 12;
+$offcanvas-overlap-zindex: 13;
+$offcanvas-reveal-zindex: 12;
+$offcanvas-transition-length: 0.5s;
+$offcanvas-transition-timing: ease;
+$offcanvas-fixed-reveal: true;
+$offcanvas-exit-background: rgba($white, 0.25);
+$maincontent-class: 'off-canvas-content';
+
+// 26. Orbit
+// ---------
+
+$orbit-bullet-background: $medium-gray;
+$orbit-bullet-background-active: $dark-gray;
+$orbit-bullet-diameter: 1.2rem;
+$orbit-bullet-margin: 0.1rem;
+$orbit-bullet-margin-top: 0.8rem;
+$orbit-bullet-margin-bottom: 0.8rem;
+$orbit-caption-background: rgba($black, 0.5);
+$orbit-caption-padding: 1rem;
+$orbit-control-background-hover: rgba($black, 0.5);
+$orbit-control-padding: 1rem;
+$orbit-control-zindex: 10;
+
+// 27. Pagination
+// --------------
+
+$pagination-font-size: rem-calc(14);
+$pagination-margin-bottom: $global-margin;
+$pagination-item-color: $black;
+$pagination-item-padding: rem-calc(3 10);
+$pagination-item-spacing: rem-calc(1);
+$pagination-radius: $global-radius;
+$pagination-item-background-hover: $light-gray;
+$pagination-item-background-current: $primary-color;
+$pagination-item-color-current: $white;
+$pagination-item-color-disabled: $medium-gray;
+$pagination-ellipsis-color: $black;
+$pagination-mobile-items: false;
+$pagination-mobile-current-item: false;
+$pagination-arrows: true;
+$pagination-arrow-previous: '\00AB';
+$pagination-arrow-next: '\00BB';
+
+// 28. Progress Bar
+// ----------------
+
+$progress-height: 1rem;
+$progress-background: $medium-gray;
+$progress-margin-bottom: $global-margin;
+$progress-meter-background: $primary-color;
+$progress-radius: $global-radius;
+
+// 29. Prototype Arrow
+// -------------------
+
+$prototype-arrow-directions: (
+ down,
+ up,
+ right,
+ left
+);
+$prototype-arrow-size: 0.4375rem;
+$prototype-arrow-color: $black;
+
+// 30. Prototype Border-Box
+// ------------------------
+
+$prototype-border-box-breakpoints: $global-prototype-breakpoints;
+
+// 31. Prototype Border-None
+// -------------------------
+
+$prototype-border-none-breakpoints: $global-prototype-breakpoints;
+
+// 32. Prototype Bordered
+// ----------------------
+
+$prototype-bordered-breakpoints: $global-prototype-breakpoints;
+$prototype-border-width: rem-calc(1);
+$prototype-border-type: solid;
+$prototype-border-color: $medium-gray;
+
+// 33. Prototype Display
+// ---------------------
+
+$prototype-display-breakpoints: $global-prototype-breakpoints;
+$prototype-display: (
+ inline,
+ inline-block,
+ block,
+ table,
+ table-cell
+);
+
+// 34. Prototype Font-Styling
+// --------------------------
+
+$prototype-font-breakpoints: $global-prototype-breakpoints;
+$prototype-wide-letter-spacing: rem-calc(4);
+$prototype-font-normal: $global-weight-normal;
+$prototype-font-bold: $global-weight-bold;
+
+// 35. Prototype List-Style-Type
+// -----------------------------
+
+$prototype-list-breakpoints: $global-prototype-breakpoints;
+$prototype-style-type-unordered: (
+ disc,
+ circle,
+ square
+);
+$prototype-style-type-ordered: (
+ decimal,
+ lower-alpha,
+ lower-latin,
+ lower-roman,
+ upper-alpha,
+ upper-latin,
+ upper-roman
+);
+
+// 36. Prototype Overflow
+// ----------------------
+
+$prototype-overflow-breakpoints: $global-prototype-breakpoints;
+$prototype-overflow: (
+ visible,
+ hidden,
+ scroll
+);
+
+// 37. Prototype Position
+// ----------------------
+
+$prototype-position-breakpoints: $global-prototype-breakpoints;
+$prototype-position: (
+ static,
+ relative,
+ absolute,
+ fixed
+);
+$prototype-position-z-index: 975;
+
+// 38. Prototype Rounded
+// ---------------------
+
+$prototype-rounded-breakpoints: $global-prototype-breakpoints;
+$prototype-border-radius: rem-calc(3);
+
+// 39. Prototype Separator
+// -----------------------
+
+$prototype-separator-breakpoints: $global-prototype-breakpoints;
+$prototype-separator-align: center;
+$prototype-separator-height: rem-calc(2);
+$prototype-separator-width: 3rem;
+$prototype-separator-background: $primary-color;
+$prototype-separator-margin-top: $global-margin;
+
+// 40. Prototype Shadow
+// --------------------
+
+$prototype-shadow-breakpoints: $global-prototype-breakpoints;
+$prototype-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),
+ 0 2px 10px 0 rgba(0,0,0,.12);
+
+// 41. Prototype Sizing
+// --------------------
+
+$prototype-sizing-breakpoints: $global-prototype-breakpoints;
+$prototype-sizing: (
+ width,
+ height
+);
+$prototype-sizes: (
+ 25: 25%,
+ 50: 50%,
+ 75: 75%,
+ 100: 100%
+);
+
+// 42. Prototype Spacing
+// ---------------------
+
+$prototype-spacing-breakpoints: $global-prototype-breakpoints;
+$prototype-spacers-count: 3;
+
+// 43. Prototype Text-Decoration
+// -----------------------------
+
+$prototype-decoration-breakpoints: $global-prototype-breakpoints;
+$prototype-text-decoration: (
+ overline,
+ underline,
+ line-through,
+);
+
+// 44. Prototype Text-Transformation
+// ---------------------------------
+
+$prototype-transformation-breakpoints: $global-prototype-breakpoints;
+$prototype-text-transformation: (
+ lowercase,
+ uppercase,
+ capitalize
+);
+
+// 45. Prototype Text-Utilities
+// ----------------------------
+
+$prototype-utilities-breakpoints: $global-prototype-breakpoints;
+$prototype-text-overflow: ellipsis;
+
+// 46. Responsive Embed
+// --------------------
+
+$responsive-embed-margin-bottom: rem-calc(16);
+$responsive-embed-ratios: (
+ default: 4 by 3,
+ widescreen: 16 by 9,
+);
+
+// 47. Reveal
+// ----------
+
+$reveal-background: $white;
+$reveal-width: 600px;
+$reveal-max-width: $global-width;
+$reveal-padding: $global-padding;
+$reveal-border: 1px solid $medium-gray;
+$reveal-radius: $global-radius;
+$reveal-zindex: 1005;
+$reveal-overlay-background: rgba($black, 0.45);
+
+// 48. Slider
+// ----------
+
+$slider-width-vertical: 0.5rem;
+$slider-transition: all 0.2s ease-in-out;
+$slider-height: 0.5rem;
+$slider-background: $light-gray;
+$slider-fill-background: $medium-gray;
+$slider-handle-height: 1.4rem;
+$slider-handle-width: 1.4rem;
+$slider-handle-background: $primary-color;
+$slider-opacity-disabled: 0.25;
+$slider-radius: $global-radius;
+
+// 49. Switch
+// ----------
+
+$switch-background: $medium-gray;
+$switch-background-active: $primary-color;
+$switch-height: 2rem;
+$switch-height-tiny: 1.5rem;
+$switch-height-small: 1.75rem;
+$switch-height-large: 2.5rem;
+$switch-radius: $global-radius;
+$switch-margin: $global-margin;
+$switch-paddle-background: $white;
+$switch-paddle-offset: 0.25rem;
+$switch-paddle-radius: $global-radius;
+$switch-paddle-transition: all 0.25s ease-out;
+$switch-opacity-disabled: .5;
+$switch-cursor-disabled: not-allowed;
+
+// 50. Table
+// ---------
+
+$table-background: $white;
+$table-color-scale: 5%;
+$table-border: 1px solid smart-scale($table-background, $table-color-scale);
+$table-padding: rem-calc(8 10 10);
+$table-hover-scale: 2%;
+$table-row-hover: darken($table-background, $table-hover-scale);
+$table-row-stripe-hover: darken($table-background, $table-color-scale + $table-hover-scale);
+$table-is-striped: true;
+$table-striped-background: smart-scale($table-background, $table-color-scale);
+$table-stripe: even;
+$table-head-background: smart-scale($table-background, $table-color-scale / 2);
+$table-head-row-hover: darken($table-head-background, $table-hover-scale);
+$table-foot-background: smart-scale($table-background, $table-color-scale);
+$table-foot-row-hover: darken($table-foot-background, $table-hover-scale);
+$table-head-font-color: $body-font-color;
+$table-foot-font-color: $body-font-color;
+$show-header-for-stacked: false;
+$table-stack-breakpoint: medium;
+
+// 51. Tabs
+// --------
+
+$tab-margin: 0;
+$tab-background: $white;
+$tab-color: $primary-color;
+$tab-background-active: $light-gray;
+$tab-active-color: $primary-color;
+$tab-item-font-size: rem-calc(12);
+$tab-item-background-hover: $white;
+$tab-item-padding: 1.25rem 1.5rem;
+$tab-content-background: $white;
+$tab-content-border: $light-gray;
+$tab-content-color: $body-font-color;
+$tab-content-padding: 1rem;
+
+// 52. Thumbnail
+// -------------
+
+$thumbnail-border: 4px solid $white;
+$thumbnail-margin-bottom: $global-margin;
+$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2);
+$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5);
+$thumbnail-transition: box-shadow 200ms ease-out;
+$thumbnail-radius: $global-radius;
+
+// 53. Title Bar
+// -------------
+
+$titlebar-background: $black;
+$titlebar-color: $white;
+$titlebar-padding: 0.5rem;
+$titlebar-text-font-weight: bold;
+$titlebar-icon-color: $white;
+$titlebar-icon-color-hover: $medium-gray;
+$titlebar-icon-spacing: 0.25rem;
+
+// 54. Tooltip
+// -----------
+
+$has-tip-cursor: help;
+$has-tip-font-weight: $global-weight-bold;
+$has-tip-border-bottom: dotted 1px $dark-gray;
+$tooltip-background-color: $black;
+$tooltip-color: $white;
+$tooltip-padding: 0.75rem;
+$tooltip-max-width: 10rem;
+$tooltip-font-size: $small-font-size;
+$tooltip-pip-width: 0.75rem;
+$tooltip-pip-height: $tooltip-pip-width * 0.866;
+$tooltip-radius: $global-radius;
+
+// 55. Top Bar
+// -----------
+
+$topbar-padding: 0.5rem;
+$topbar-background: $light-gray;
+$topbar-submenu-background: $topbar-background;
+$topbar-title-spacing: 0.5rem 1rem 0.5rem 0;
+$topbar-input-width: 200px;
+$topbar-unstack-breakpoint: medium;
+
+// 56. Xy Grid
+// -----------
+
+$xy-grid: true;
+$grid-container: $global-width;
+$grid-columns: 12;
+$grid-margin-gutters: (
+ small: 20px,
+ medium: 30px
+);
+$grid-padding-gutters: $grid-margin-gutters;
+$grid-container-padding: $grid-padding-gutters;
+$grid-container-max: $global-width;
+$xy-block-grid-max: 8;
+
diff --git a/src/foundation/components/_accordion-menu.scss b/src/foundation/components/_accordion-menu.scss
new file mode 100644
index 0000000..1e05469
--- /dev/null
+++ b/src/foundation/components/_accordion-menu.scss
@@ -0,0 +1,174 @@
+////
+/// @group accordion-menu
+////
+
+/// Sets accordion menu padding.
+/// @type Number
+$accordionmenu-padding: $global-menu-padding !default;
+
+/// Sets accordion menu nested margin
+/// @type Number
+$accordionmenu-nested-margin: $global-menu-nested-margin !default;
+
+/// Sets accordion menu submenu padding.
+/// @type Number
+$accordionmenu-submenu-padding: $accordionmenu-padding !default;
+
+/// Sets if accordion menus have the default arrow styles.
+/// @type Boolean
+$accordionmenu-arrows: true !default;
+
+/// Sets accordion menu arrow color if arrow is used.
+/// @type Color
+$accordionmenu-arrow-color: $primary-color !default;
+
+/// Sets accordion menu item padding.
+/// @type Color
+$accordionmenu-item-background: null !default;
+
+/// Sets accordion menu item border.
+/// @type Color
+$accordionmenu-border: null !default;
+
+/// Sets accordion menu item padding.
+/// @type Color
+$accordionmenu-submenu-toggle-background: null !default;
+
+/// Sets accordion menu item padding.
+/// @type List
+$accordion-submenu-toggle-border: $accordionmenu-border !default;
+
+/// Sets accordion menu submenu toggle background width.
+/// @type Number
+$accordionmenu-submenu-toggle-width: 40px !default;
+
+/// Sets accordion menu submenu toggle background height.
+/// @type Number
+$accordionmenu-submenu-toggle-height: $accordionmenu-submenu-toggle-width !default;
+
+/// Sets accordion menu arrow size if arrow is used.
+/// @type Length
+$accordionmenu-arrow-size: 6px !default;
+
+@mixin zf-accordion-menu-left-right-arrows {
+ .is-accordion-submenu-parent:not(.has-submenu-toggle) > a {
+ position: relative;
+
+ &::after {
+ @include css-triangle($accordionmenu-arrow-size, $accordionmenu-arrow-color, down);
+ position: absolute;
+ top: 50%;
+ margin-top: -1 * ($accordionmenu-arrow-size / 2);
+ #{$global-right}: 1rem;
+ }
+ }
+
+ &.align-left .is-accordion-submenu-parent > a::after {
+ right: 1rem;
+ left: auto;
+ }
+
+ &.align-right .is-accordion-submenu-parent > a::after {
+ right: auto;
+ left: 1rem;
+ }
+}
+@mixin foundation-accordion-menu {
+
+ .accordion-menu {
+ @if $accordionmenu-border {
+ border-bottom: $accordionmenu-border;
+ }
+
+ li {
+ @if $accordionmenu-border {
+ border-top: $accordionmenu-border;
+ border-right: $accordionmenu-border;
+ border-left: $accordionmenu-border;
+ }
+ width: 100%;
+ }
+
+ a {
+ @if $accordionmenu-item-background {
+ background: $accordionmenu-item-background;
+ }
+ padding: $accordionmenu-padding;
+ }
+
+ .is-accordion-submenu a {
+ padding: $accordionmenu-submenu-padding;
+ }
+
+ .nested.is-accordion-submenu {
+ @include menu-nested($accordionmenu-nested-margin);
+ }
+
+ &.align-#{$global-right} {
+ .nested.is-accordion-submenu {
+ @include menu-nested($accordionmenu-nested-margin, right);
+ }
+ }
+
+ @if $accordionmenu-arrows {
+ @include zf-accordion-menu-left-right-arrows;
+
+ .is-accordion-submenu-parent[aria-expanded='true'] > a::after {
+ transform: rotate(180deg);
+ transform-origin: 50% 50%;
+ }
+ }
+ }
+
+ .is-accordion-submenu li {
+ @if $accordionmenu-border {
+ border-right: 0;
+ border-left: 0;
+ }
+ }
+
+ .is-accordion-submenu-parent {
+ position: relative;
+ }
+
+ .has-submenu-toggle > a {
+ margin-#{$global-right}: $accordionmenu-submenu-toggle-width;
+ }
+
+ // Submenu toggle
+ .submenu-toggle {
+ position: absolute;
+ top: 0;
+ #{$global-right}: 0;
+
+ width: $accordionmenu-submenu-toggle-width;
+ height: $accordionmenu-submenu-toggle-height;
+
+ cursor: pointer;
+
+ border-#{$global-left}: $accordion-submenu-toggle-border;
+
+ @if $accordionmenu-submenu-toggle-background {
+ background: $accordionmenu-submenu-toggle-background;
+ }
+
+ // Add the arrow to the toggle
+ &::after {
+ @include css-triangle(6px, $accordionmenu-arrow-color, down);
+
+ top: 0;
+ bottom: 0;
+ margin: auto;
+ }
+ }
+
+ // Rotate the arrow when menu is open
+ .submenu-toggle[aria-expanded='true']::after {
+ transform: scaleY(-1);
+ transform-origin: 50% 50%;
+ }
+
+ .submenu-toggle-text {
+ @include element-invisible;
+ }
+}
diff --git a/src/foundation/components/_accordion.scss b/src/foundation/components/_accordion.scss
new file mode 100644
index 0000000..b7c0c58
--- /dev/null
+++ b/src/foundation/components/_accordion.scss
@@ -0,0 +1,164 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group accordion
+////
+
+/// Default background color of an accordion group.
+/// @type Color
+$accordion-background: $white !default;
+
+/// If `true`, adds plus and minus icons to the side of each accordion title.
+/// @type Boolean
+$accordion-plusminus: true !default;
+
+/// Content for the plus icon when `$accordion-plusminus` is `true`
+/// @type String
+$accordion-plus-content: '\002B' !default;
+
+/// Content for the minus icon when `$accordion-plusminus` is `true`
+/// @type String
+$accordion-minus-content: '\2013' !default;
+
+/// Font size of accordion titles.
+/// @type Number
+$accordion-title-font-size: rem-calc(12) !default;
+
+/// Default text color for items in a Menu.
+/// @type Color
+$accordion-item-color: $primary-color !default;
+
+/// Default background color on hover for items in a Menu.
+/// @type Color
+$accordion-item-background-hover: $light-gray !default;
+
+/// Default padding of an accordion item.
+/// @type Number | List
+$accordion-item-padding: 1.25rem 1rem !default;
+
+/// Default background color of tab content.
+/// @type Color
+$accordion-content-background: $white !default;
+
+/// Default border color of tab content.
+/// @type Color
+$accordion-content-border: 1px solid $light-gray !default;
+
+/// Default text color of tab content.
+/// @type Color
+$accordion-content-color: $body-font-color !default;
+
+/// Default padding for tab content.
+/// @type Number | List
+$accordion-content-padding: 1rem !default;
+
+/// Adds styles for an accordion container. Apply this to the same element that gets `data-accordion`.
+@mixin accordion-container (
+ $background: $accordion-background
+) {
+ margin-#{$global-left}: 0;
+ background: $background;
+ list-style-type: none;
+
+ &[disabled] {
+ .accordion-title {
+ cursor: not-allowed;
+ }
+ }
+}
+
+/// Adds styles for the accordion item. Apply this to the list item within an accordion ul.
+@mixin accordion-item {
+ &:first-child > :first-child {
+ border-radius: $global-radius $global-radius 0 0;
+ }
+
+ &:last-child > :last-child {
+ border-radius: 0 0 $global-radius $global-radius;
+ }
+}
+
+/// Adds styles for the title of an accordion item. Apply this to the link within an accordion item.
+@mixin accordion-title (
+ $padding: $accordion-item-padding,
+ $font-size: $accordion-title-font-size,
+ $color: $accordion-item-color,
+ $border: $accordion-content-border,
+ $background-hover: $accordion-item-background-hover
+) {
+ position: relative;
+ display: block;
+ padding: $padding;
+
+ border: $border;
+ border-bottom: 0;
+
+ font-size: $font-size;
+ line-height: 1;
+ color: $color;
+
+ :last-child:not(.is-active) > & {
+ border-bottom: $border;
+ border-radius: 0 0 $global-radius $global-radius;
+ }
+
+ &:hover,
+ &:focus {
+ background-color: $background-hover;
+ }
+
+ @if $accordion-plusminus {
+ &::before {
+ position: absolute;
+ top: 50%;
+ #{$global-right}: 1rem;
+ margin-top: -0.5rem;
+ content: $accordion-plus-content;
+ }
+
+ .is-active > &::before {
+ content: $accordion-minus-content;
+ }
+ }
+}
+
+/// Adds styles for accordion content. Apply this to the content pane below an accordion item's title.
+@mixin accordion-content (
+ $padding: $accordion-content-padding,
+ $border: $accordion-content-border,
+ $background: $accordion-content-background,
+ $color: $accordion-content-color
+) {
+ display: none;
+ padding: $padding;
+
+ border: $border;
+ border-bottom: 0;
+ background-color: $background;
+
+ color: $color;
+
+ :last-child > &:last-child {
+ border-bottom: $border;
+ }
+}
+
+@mixin foundation-accordion {
+ .accordion {
+ @include accordion-container;
+ }
+
+ .accordion-item {
+ @include accordion-item;
+ }
+
+ .accordion-title {
+ @include accordion-title;
+ }
+
+ .accordion-content {
+ @include accordion-content;
+ }
+}
diff --git a/src/foundation/components/_badge.scss b/src/foundation/components/_badge.scss
new file mode 100644
index 0000000..3d5b6ba
--- /dev/null
+++ b/src/foundation/components/_badge.scss
@@ -0,0 +1,63 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group badge
+////
+
+/// Default background color for badges.
+/// @type Color
+$badge-background: $primary-color !default;
+
+/// Default text color for badges.
+/// @type Color
+$badge-color: $white !default;
+
+/// Alternate text color for badges.
+/// @type Color
+$badge-color-alt: $black !default;
+
+/// Coloring classes. A map of classes to output in your CSS, like `.secondary`, `.success`, and so on.
+/// @type Map
+$badge-palette: $foundation-palette !default;
+
+/// Default padding inside badges.
+/// @type Number
+$badge-padding: 0.3em !default;
+
+/// Minimum width of a badge.
+/// @type Number
+$badge-minwidth: 2.1em !default;
+
+/// Default font size for badges.
+/// @type Number
+$badge-font-size: 0.6rem !default;
+
+/// Generates the base styles for a badge.
+@mixin badge {
+ display: inline-block;
+ min-width: $badge-minwidth;
+ padding: $badge-padding;
+
+ border-radius: 50%;
+
+ font-size: $badge-font-size;
+ text-align: center;
+}
+
+@mixin foundation-badge {
+ .badge {
+ @include badge;
+
+ background: $badge-background;
+ color: $badge-color;
+
+ @each $name, $color in $badge-palette {
+ &.#{$name} {
+ background: $color;
+ color: color-pick-contrast($color, ($badge-color, $badge-color-alt));
+ }
+ }
+ }
+}
diff --git a/src/foundation/components/_breadcrumbs.scss b/src/foundation/components/_breadcrumbs.scss
new file mode 100644
index 0000000..e48cc0b
--- /dev/null
+++ b/src/foundation/components/_breadcrumbs.scss
@@ -0,0 +1,119 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group breadcrumbs
+////
+
+/// Margin around a breadcrumbs container.
+/// @type Number
+$breadcrumbs-margin: 0 0 $global-margin 0 !default;
+
+/// Font size of breadcrumb links.
+/// @type Number
+$breadcrumbs-item-font-size: rem-calc(11) !default;
+
+/// Color of breadcrumb links.
+/// @type Color
+$breadcrumbs-item-color: $primary-color !default;
+
+/// Color of the active breadcrumb link.
+/// @type Color
+$breadcrumbs-item-color-current: $black !default;
+
+/// Opacity of disabled breadcrumb links.
+/// @type Number
+$breadcrumbs-item-color-disabled: $medium-gray !default;
+
+/// Margin between breadcrumb items.
+/// @type Number
+$breadcrumbs-item-margin: 0.75rem !default;
+
+/// If `true`, makes breadcrumb links uppercase.
+/// @type Boolean
+$breadcrumbs-item-uppercase: true !default;
+
+/// If `true`, adds a seperator between breadcrumb links.
+/// @type Boolean
+$breadcrumbs-item-separator: true !default;
+
+// If it exists $breadcrumbs-item-slash is used to build $breadcrumbs-item-separator. See the documentation.
+@if variable-exists(breadcrumbs-item-slash) {
+ $breadcrumbs-item-separator: $breadcrumbs-item-slash;
+}
+
+/// Used character for the breadcrumb separator.
+/// @type Content
+$breadcrumbs-item-separator-item: '/' !default;
+
+/// Used character for the breadcrumb separator in rtl mode.
+/// @type Content
+$breadcrumbs-item-separator-item-rtl: '\\' !default;
+
+/// Color of breadcrumb item.
+/// @type Color
+$breadcrumbs-item-separator-color: $medium-gray !default;
+
+// If it exists $breadcrumbs-item-slash-color is used to build $breadcrumbs-item-separator-color. See the documentation.
+@if variable-exists(breadcrumbs-item-slash-color) {
+ $breadcrumbs-item-separator-color: $breadcrumbs-item-slash-color;
+}
+
+/// Adds styles for a breadcrumbs container, along with the styles for the `<li>` and `<a>` elements inside of it.
+@mixin breadcrumbs-container {
+ @include clearfix;
+ margin: $breadcrumbs-margin;
+ list-style: none;
+
+ // Item wrapper
+ li {
+ float: #{$global-left};
+
+ font-size: $breadcrumbs-item-font-size;
+ color: $breadcrumbs-item-color-current;
+ cursor: default;
+
+ @if $breadcrumbs-item-uppercase {
+ text-transform: uppercase;
+ }
+
+ @if $breadcrumbs-item-separator {
+ // Need to escape the backslash
+ $separator: if($global-text-direction == 'ltr', $breadcrumbs-item-separator-item, $breadcrumbs-item-separator-item-rtl);
+
+ &:not(:last-child) {
+ &::after {
+ position: relative;
+ margin: 0 $breadcrumbs-item-margin;
+ opacity: 1;
+ content: $separator;
+ color: $breadcrumbs-item-separator-color;
+ }
+ }
+ }
+ @else {
+ margin-#{$global-right}: $breadcrumbs-item-margin;
+ }
+ }
+
+ // Page links
+ a {
+ color: $breadcrumbs-item-color;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+}
+
+@mixin foundation-breadcrumbs {
+ .breadcrumbs {
+ @include breadcrumbs-container;
+
+ .disabled {
+ color: $breadcrumbs-item-color-disabled;
+ cursor: not-allowed;
+ }
+ }
+}
diff --git a/src/foundation/components/_button-group.scss b/src/foundation/components/_button-group.scss
new file mode 100644
index 0000000..59f432f
--- /dev/null
+++ b/src/foundation/components/_button-group.scss
@@ -0,0 +1,299 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group button-group
+////
+
+/// Margin for button groups.
+/// @type Number
+$buttongroup-margin: 1rem !default;
+
+/// Margin between buttons in a button group.
+/// @type Number
+$buttongroup-spacing: 1px !default;
+
+/// Selector for the buttons inside a button group.
+/// @type String
+$buttongroup-child-selector: '.button' !default;
+
+/// Maximum number of buttons that can be in an even-width button group. (Only needed when $global-flexbox: false;)
+/// @type Number
+$buttongroup-expand-max: 6 !default;
+
+/// Determines if $button-radius is applied to each button or the button group as a whole. Use $global-radius in _settings.scss to change radius.
+/// @type Boolean
+$buttongroup-radius-on-each: true !default;
+
+/// Add styles for a button group container.
+/// @param {String} $child-selector [$buttongroup-child-selector] - Selector for the buttons inside a button group.
+/// @param {Number} $spacing [$buttongroup-spacing] - Spacing between buttons in a button group.
+@mixin button-group(
+ $child-selector: $buttongroup-child-selector,
+ $spacing: $buttongroup-spacing
+) {
+ @include clearfix;
+ margin-bottom: $buttongroup-margin;
+
+ @if $global-flexbox {
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: stretch;
+ }
+ @else {
+ font-size: 0;
+ }
+
+ #{$child-selector} {
+ margin: 0;
+ margin-#{$global-right}: $spacing;
+ margin-bottom: $spacing;
+ font-size: map-get($button-sizes, default);
+
+ @if $global-flexbox {
+ flex: 0 0 auto;
+ }
+
+ &:last-child {
+ margin-#{$global-right}: 0;
+ }
+
+ @if not $buttongroup-radius-on-each {
+ border-radius: 0;
+
+ &:first-child {
+ border-top-#{$global-left}-radius: $button-radius;
+ border-bottom-#{$global-left}-radius: $button-radius;
+ }
+
+ &:last-child {
+ border-top-#{$global-right}-radius: $button-radius;
+ border-bottom-#{$global-right}-radius: $button-radius;
+ }
+ }
+ }
+}
+
+/// Make buttons bonded without gap between them. Borders between buttons are merged
+/// @param {String} $selector [$buttongroup-child-selector] - Selector for the buttons inside a button group.
+@mixin button-group-no-gaps(
+ $selector: $buttongroup-child-selector,
+ $border-width: $button-hollow-border-width
+) {
+ #{$selector} {
+ margin-#{$global-right}: rem-calc(-$border-width);
+
+ + #{$selector} {
+ border-#{$global-left}-color: transparent;
+ }
+ }
+}
+
+/// Creates a full-width button group, making each button equal width.
+/// @param {String} $selector [$buttongroup-child-selector] - Selector for the buttons inside a button group.
+/// @param {Number} $spacing [$buttongroup-spacing] - Spacing between buttons in a button group.
+@mixin button-group-expand(
+ $selector: $buttongroup-child-selector,
+ $spacing: $buttongroup-spacing,
+ $count: null
+) {
+ @if not $global-flexbox {
+ margin-#{$global-right}: -$spacing;
+
+ &::before,
+ &::after {
+ display: none;
+ }
+ }
+
+ #{$selector} {
+ @if $global-flexbox {
+ flex: 1 1 0px; // sass-lint:disable-line zero-unit
+ }
+ @else {
+ // One child
+ &:first-child {
+ &:last-child {
+ width: calc(100% - #{$spacing});
+ }
+ }
+
+ // Two or more childreen
+ @for $i from 2 through $buttongroup-expand-max {
+ &:first-child:nth-last-child(#{$i}) {
+ &, &:first-child:nth-last-child(#{$i}) ~ #{$selector} {
+ display: inline-block;
+ width: calc(#{percentage(1 / $i)} - #{$spacing});
+ margin-#{$global-right}: $spacing;
+
+ &:last-child {
+ margin-#{$global-right}: $spacing * -$buttongroup-expand-max;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/// Stacks the buttons in a button group.
+/// @param {String} $selector [$buttongroup-child-selector] - Selector for the buttons inside the button group.
+@mixin button-group-stack(
+ $selector: $buttongroup-child-selector
+) {
+ @if $global-flexbox {
+ flex-wrap: wrap;
+ }
+
+ #{$selector} {
+ @if $global-flexbox {
+ flex: 0 0 100%;
+ }
+ @else {
+ width: 100%;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+
+ @if not $buttongroup-radius-on-each {
+ border-radius: 0;
+
+ &:first-child {
+ border-top-#{$global-left}-radius: $global-radius;
+ border-top-#{$global-right}-radius: $global-radius;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ border-bottom-#{$global-left}-radius: $global-radius;
+ border-bottom-#{$global-right}-radius: $global-radius;
+ }
+ }
+
+ }
+}
+
+/// Un-stacks the buttons in a button group.
+/// @param {String} $selector [$buttongroup-child-selector] - Selector for the buttons inside the button group.
+@mixin button-group-unstack(
+ $selector: $buttongroup-child-selector
+) {
+ #{$selector} {
+ @if $global-flexbox {
+ flex: 0 0 auto;
+ }
+ @else {
+ width: auto;
+ }
+ margin-bottom: 0;
+
+ @if not $buttongroup-radius-on-each {
+ &:first-child {
+ border-top-#{$global-left}-radius: $global-radius;
+ border-top-#{$global-right}-radius: 0;
+ border-bottom-#{$global-left}-radius: $global-radius;
+ }
+
+ &:last-child {
+ border-top-#{$global-right}-radius: $global-radius;
+ border-bottom-#{$global-right}-radius: $global-radius;
+ border-bottom-#{$global-left}-radius: 0;
+ }
+ }
+
+ }
+}
+
+@mixin foundation-button-group {
+ .button-group {
+ @include button-group;
+
+ // Sizes
+ @each $size, $value in map-remove($button-sizes, default) {
+ &.#{$size} #{$buttongroup-child-selector} {
+ font-size: $value;
+ }
+ }
+
+ // Even-width Group
+ &.expanded {
+ @include button-group-expand;
+ }
+
+ // Solid, hollow & clear styles
+ @each $filling in (solid hollow clear) {
+ $base-selector: if($button-fill == $filling, null, '.#{$filling}');
+
+ &#{$base-selector} {
+ // Do not generate button base styles for the default filling
+ @if($button-fill != $filling) {
+ #{$buttongroup-child-selector} {
+ @include button-fill($filling);
+ @include button-fill-style($filling);
+ }
+ }
+
+ @each $name, $color in $button-palette {
+ $individual-selector: if($button-fill == $filling, null, ' #{$buttongroup-child-selector}.#{$name}');
+
+ &.#{$name} #{$buttongroup-child-selector}, #{$individual-selector} {
+ @include button-fill-style($filling, $color, auto, auto);
+ }
+ }
+ }
+
+ }
+
+ &.no-gaps {
+ @include button-group-no-gaps;
+ }
+
+ &.stacked,
+ &.stacked-for-small,
+ &.stacked-for-medium {
+ @include button-group-stack;
+
+ &.expanded {
+ @include button-group-expand;
+ }
+ }
+
+ &.stacked-for-small {
+ @include breakpoint(medium) {
+ @include button-group-unstack;
+ }
+ }
+
+ &.stacked-for-medium {
+ @include breakpoint(large) {
+ @include button-group-unstack;
+ }
+ }
+
+ &.stacked-for-small.expanded {
+ @include breakpoint(small only) {
+ display: block;
+
+ #{$buttongroup-child-selector} {
+ display: block;
+ margin-#{$global-right}: 0;
+ }
+ }
+ }
+
+ &.stacked-for-medium.expanded {
+ @include breakpoint(medium down) {
+ display: block;
+
+ #{$buttongroup-child-selector} {
+ display: block;
+ margin-#{$global-right}: 0;
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/components/_button.scss b/src/foundation/components/_button.scss
new file mode 100644
index 0000000..e79aab1
--- /dev/null
+++ b/src/foundation/components/_button.scss
@@ -0,0 +1,428 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group button
+////
+
+/// Font family for button elements.
+/// @type Font
+$button-font-family: inherit !default;
+
+/// Font weight for button elements.
+/// Ignored if null (default)
+/// @type Font-Weight
+$button-font-weight: null !default;
+
+/// Padding inside buttons.
+/// @type List
+$button-padding: 0.85em 1em !default;
+
+/// Margin around buttons.
+/// @type List
+$button-margin: 0 0 $global-margin 0 !default;
+
+/// Default fill for buttons. Can either be `solid` or `hollow`.
+/// @type Keyword
+$button-fill: solid !default;
+
+/// Default background color for buttons.
+/// @type Color
+$button-background: $primary-color !default;
+
+/// Background color on hover for buttons.
+/// @type Color
+$button-background-hover: scale-color($button-background, $lightness: -15%) !default;
+
+/// Font color for buttons.
+/// @type List
+$button-color: $white !default;
+
+/// Alternative font color for buttons.
+/// @type List
+$button-color-alt: $black !default;
+
+/// Border radius for buttons, defaulted to global-radius.
+/// @type Number
+$button-radius: $global-radius !default;
+
+/// Border for buttons, transparent by default
+/// @type List
+$button-border: 1px solid transparent !default;
+
+/// Border width for hollow outline buttons
+/// @type Number
+$button-hollow-border-width: 1px !default;
+
+/// Sizes for buttons.
+/// @type Map
+$button-sizes: (
+ tiny: 0.6rem,
+ small: 0.75rem,
+ default: 0.9rem,
+ large: 1.25rem,
+) !default;
+
+/// Coloring classes. A map of classes to output in your CSS, like `.secondary`, `.success`, and so on.
+/// @type Map
+$button-palette: $foundation-palette !default;
+
+/// opacity for a disabled button.
+/// @type List
+$button-opacity-disabled: 0.25 !default;
+
+/// Background color lightness on hover for buttons.
+/// @type Number
+$button-background-hover-lightness: -20% !default;
+
+/// Color lightness on hover for hollow buttons.
+/// @type Number
+$button-hollow-hover-lightness: -50% !default;
+
+// Internal: flip from margin-right to margin-left for defaults
+@if $global-text-direction == 'rtl' {
+ $button-margin: 0 0 $global-margin $global-margin !default;
+}
+
+/// transitions for buttons.
+/// @type List
+$button-transition: background-color 0.25s ease-out, color 0.25s ease-out !default;
+
+/// Additional responsive classes for .expanded
+/// @type Boolean
+$button-responsive-expanded: false !default;
+
+// TODO: Document button-base() mixin
+@mixin button-base {
+ @include disable-mouse-outline;
+ display: inline-block;
+ vertical-align: middle;
+ margin: $button-margin;
+
+ @if (type-of($button-padding) == 'map') {
+ @each $size, $padding in $button-padding {
+ @include breakpoint($size) {
+ padding: $padding;
+ }
+ }
+ }
+ @else {
+ padding: $button-padding;
+ }
+
+ border: $button-border;
+ border-radius: $button-radius;
+ transition: $button-transition;
+ font-family: $button-font-family;
+ font-size: map-get($button-sizes, default);
+ font-weight: $button-font-weight;
+ -webkit-appearance: none; // sass-lint:disable-line no-vendor-prefixes
+ line-height: 1;
+ text-align: center;
+ cursor: pointer;
+}
+
+/// Expands a button to make it full-width.
+/// @param {Boolean} $expand [true] - Set to `true` to enable the expand behavior. Set to `false` to reverse this behavior.
+@mixin button-expand($expand: true) {
+ @if $expand {
+ display: block;
+ width: 100%;
+ margin-right: 0;
+ margin-left: 0;
+ }
+ @else {
+ display: inline-block;
+ width: auto;
+ margin: $button-margin;
+ }
+}
+
+/// Sets the base styles of a hollow or clear button filling according to `$fill`.
+/// See mixin `button-fill-style` for the filling styles.
+/// @param {Keyword} $fill [$button-fill] - Type of filling between `hollow` and `clear`. `solid` has no effects.
+@mixin button-fill(
+ $fill: $button-fill
+) {
+ @if $fill == hollow {
+ @include button-hollow;
+ }
+ @else if $fill == clear {
+ @include button-clear;
+ }
+}
+
+/// Sets the visual styles of a solid/hollow/clear button filling according to `$fill`.
+/// See mixins `button-style`, `button-hollow-style` and `button-clear-style` for effects of visual styling parameters.
+/// @param {Keyword} $fill [$button-fill] - Type of filling between `hollow` and `clear`.
+/// @param {Color} $background [$button-background] - -
+/// @param {Color} $background-hover [$button-background-hover] - -
+/// @param {Color} $color [$button-color] - -
+@mixin button-fill-style(
+ $fill: $button-fill,
+ $background: $button-background,
+ $background-hover: $button-background-hover,
+ $color: $button-color
+) {
+ @if $fill == solid {
+ @include button-style($background, $background-hover, $color);
+ }
+ @else if $fill == hollow {
+ @include button-hollow-style($background);
+ }
+ @else if $fill == clear {
+ @include button-clear-style($background);
+ }
+}
+
+/// Sets the visual style of a button.
+/// @param {Color} $background [$button-background] - Background color of the button.
+/// @param {Color} $background-hover [$button-background-hover] - Background color of the button on hover. Set to `auto` to have the mixin automatically generate a hover color.
+/// @param {Color} $color [$button-color] - Text color of the button. Set to `auto` to automatically generate a color based on the background color.
+@mixin button-style(
+ $background: $button-background,
+ $background-hover: $button-background-hover,
+ $color: $button-color,
+ $background-hover-lightness: $button-background-hover-lightness
+) {
+ @if $color == auto {
+ $color: color-pick-contrast($background, ($button-color, $button-color-alt));
+ }
+
+ @if $background-hover == auto {
+ $background-hover: scale-color($background, $lightness: $background-hover-lightness);
+ }
+
+ // Default and disabled states
+ &,
+ &.disabled, &[disabled],
+ &.disabled:hover, &[disabled]:hover,
+ &.disabled:focus, &[disabled]:focus {
+ background-color: $background;
+ color: $color;
+ }
+
+ &:hover, &:focus {
+ background-color: $background-hover;
+ color: $color;
+ }
+}
+
+/// Sets the base styles of a hollow button.
+/// See mixin `button-hollow-style` for the filling styles.
+@mixin button-hollow {
+ &, &.disabled, &[disabled] {
+ &, &:hover, &:focus {
+ background-color: transparent;
+ }
+ }
+}
+
+/// Sets the visual style of a hollow button.
+/// @param {Color} $color [$button-background] - Text and border color of the button.
+/// @param {Color} $hover-lightness [$button-hollow-hover-lightness] - Color lightness on hover.
+/// @param {Color} $border-width [$button-hollow-border-width] - Border width of the button.
+@mixin button-hollow-style(
+ $color: $button-background,
+ $hover-lightness: $button-hollow-hover-lightness,
+ $border-width: $button-hollow-border-width
+) {
+ $color-hover: scale-color($color, $lightness: $hover-lightness);
+
+ // Default and disabled states
+ &,
+ &.disabled, &[disabled],
+ &.disabled:hover, &[disabled]:hover,
+ &.disabled:focus, &[disabled]:focus {
+ border: $border-width solid $color;
+ color: $color;
+ }
+
+ &:hover, &:focus {
+ border-color: $color-hover;
+ color: $color-hover;
+ }
+}
+
+/// Sets the base styles of a clear button.
+/// See mixin `button-clear-style` for the filling styles.
+@mixin button-clear {
+ &, &.disabled, &[disabled] {
+ &, &:hover, &:focus {
+ border-color: transparent;
+ background-color: transparent;
+ }
+ }
+}
+
+/// Sets the visual style of a clear button.
+/// @param {Color} $color [$button-background] - Text color of the button.
+/// @param {Color} $hover-lightness [$button-hollow-hover-lightness] - Color lightness on hover.
+@mixin button-clear-style(
+ $color: $button-background,
+ $hover-lightness: $button-hollow-hover-lightness
+) {
+ $color-hover: scale-color($color, $lightness: $hover-lightness);
+
+ // Default and disabled states
+ &,
+ &.disabled, &[disabled],
+ &.disabled:hover, &[disabled]:hover,
+ &.disabled:focus, &[disabled]:focus {
+ color: $color;
+ }
+
+ &:hover, &:focus {
+ color: $color-hover;
+ }
+}
+
+/// Adds disabled styles to a button by fading the element and reseting the cursor.
+/// @param {Number} $opacity [$button-opacity-disabled] - Opacity of the disabled button.
+@mixin button-disabled(
+ $opacity: $button-opacity-disabled
+) {
+ opacity: $button-opacity-disabled;
+ cursor: not-allowed;
+}
+
+/// Adds a dropdown arrow to a button.
+/// @param {Number} $size [0.4em] - Size of the arrow. We recommend using an `em` value so the triangle scales when used inside different sizes of buttons.
+/// @param {Color} $color [white] - Color of the arrow.
+/// @param {Number} $offset [$button-padding] - Distance between the arrow and the text of the button. Defaults to whatever the right padding of a button is.
+@mixin button-dropdown(
+ $size: 0.4em,
+ $color: $white,
+ $offset: get-side($button-padding, right)
+) {
+ &::after {
+ @include css-triangle($size, $color, down);
+ position: relative;
+ top: 0.4em; // Aligns the arrow with the text of the button
+
+ display: inline-block;
+ float: #{$global-right};
+ margin-#{$global-left}: $offset;
+ }
+}
+
+/// Adds all styles for a button. For more granular control over styles, use the individual button mixins.
+/// @param {Boolean} $expand [false] - Set to `true` to make the button full-width.
+/// @param {Color} $background [$button-background] - Background color of the button.
+/// @param {Color} $background-hover [$button-background-hover] - Background color of the button on hover. Set to `auto` to have the mixin automatically generate a hover color.
+/// @param {Color} $color [$button-color] - Text color of the button. Set to `auto` to automatically generate a color based on the background color.
+/// @param {Keyword} $style [solid] - Set to `hollow` to create a hollow button. The color defined in `$background` will be used as the primary color of the button.
+@mixin button(
+ $expand: false,
+ $background: $button-background,
+ $background-hover: $button-background-hover,
+ $color: $button-color,
+ $style: $button-fill
+) {
+ @include button-base;
+ @include button-fill($style);
+ @include button-fill-style($style, $background, $background-hover, $color);
+
+ @if $expand {
+ @include button-expand;
+ }
+}
+
+@mixin foundation-button {
+ .button {
+ @include button($style: none);
+
+ // Sizes
+ @each $size, $value in map-remove($button-sizes, default) {
+ &.#{$size} {
+ font-size: $value;
+ }
+ }
+
+ &.expanded { @include button-expand; }
+
+ @if $button-responsive-expanded {
+ @each $size in $breakpoint-classes {
+ @include breakpoint(#{$size} only) {
+ &.#{$size}-only-expanded {
+ @include button-expand;
+ }
+ }
+ @if $size != $-zf-zero-breakpoint {
+ @include breakpoint(#{$size} down) {
+ &.#{$size}-down-expanded {
+ @include button-expand;
+ }
+ }
+
+ @include breakpoint(#{$size}) {
+ &.#{$size}-expanded {
+ @include button-expand;
+ }
+ }
+ }
+ }
+ }
+
+ // Solid, hollow & clear styles
+ @each $filling in (solid hollow clear) {
+ $selector: if($button-fill == $filling, null, '.#{$filling}');
+
+ &#{$selector} {
+ @include button-fill($filling);
+ @include button-fill-style($filling);
+
+ @each $name, $color in $button-palette {
+ &.#{$name} {
+ @include button-fill-style($filling, $color, auto, auto);
+ }
+ }
+ }
+ }
+
+ // Disabled state
+ &.disabled, &[disabled] {
+ @include button-disabled;
+ }
+
+ // Dropdown arrow
+ &.dropdown {
+ @include button-dropdown;
+
+ @if $button-fill == hollow {
+ &::after {
+ border-top-color: $button-background;
+ }
+ }
+
+ &.hollow, &.clear {
+ &::after {
+ border-top-color: $button-background;
+ }
+
+ @each $name, $color in $button-palette {
+ &.#{$name} {
+ &::after {
+ border-top-color: $color;
+ }
+ }
+ }
+ }
+ }
+
+ // Button with dropdown arrow only
+ &.arrow-only::after {
+ top: -0.1em;
+ float: none;
+ margin-#{$global-left}: 0;
+ }
+ }
+
+ a.button { // sass-lint:disable-line no-qualifying-elements
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ }
+ }
+}
diff --git a/src/foundation/components/_callout.scss b/src/foundation/components/_callout.scss
new file mode 100644
index 0000000..979a5ad
--- /dev/null
+++ b/src/foundation/components/_callout.scss
@@ -0,0 +1,108 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group callout
+////
+
+/// Default background color.
+/// @type Color
+$callout-background: $white !default;
+
+/// Default fade value for callout backgrounds.
+/// @type Number
+$callout-background-fade: 85% !default;
+
+/// Default border style for callouts.
+/// @type List
+$callout-border: 1px solid rgba($black, 0.25) !default;
+
+/// Default bottom margin for callouts.
+/// @type Number
+$callout-margin: 0 0 1rem 0 !default;
+
+/// Sizes for Callout paddings.
+/// @type Map
+$callout-sizes: (
+ small: 0.5rem,
+ default: 1rem,
+ large: 3rem,
+) !default;
+
+/// Default font color for callouts.
+/// @type Color
+$callout-font-color: $body-font-color !default;
+
+/// Default font color for callouts, if the callout has a dark background.
+/// @type Color
+$callout-font-color-alt: $body-background !default;
+
+/// Default border radius for callouts.
+/// @type Color
+$callout-radius: $global-radius !default;
+
+/// Amount to tint links used within colored panels. Set to `false` to disable this feature.
+/// @type Number | Boolean
+$callout-link-tint: 30% !default;
+
+/// Adds basic styles for a callout, including padding and margin.
+@mixin callout-base() {
+ position: relative;
+ margin: $callout-margin;
+ padding: map-get($callout-sizes, default);
+
+ border: $callout-border;
+ border-radius: $callout-radius;
+
+ // Respect the padding, fool.
+ > :first-child {
+ margin-top: 0;
+ }
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+/// Generate quick styles for a callout using a single color as a baseline.
+/// @param {Color} $color [$callout-background] - Color to use.
+@mixin callout-style($color: $callout-background) {
+ $background: scale-color($color, $lightness: $callout-background-fade);
+
+ background-color: $background;
+ color: color-pick-contrast($background, ($callout-font-color, $callout-font-color-alt));
+}
+
+@mixin callout-size($padding) {
+ padding-top: $padding;
+ padding-right: $padding;
+ padding-bottom: $padding;
+ padding-left: $padding;
+}
+
+
+/// Adds styles for a callout.
+/// @param {Color} $color [$callout-background] - Color to use.
+@mixin callout($color: $callout-background) {
+ @include callout-base;
+ @include callout-style($color);
+}
+
+@mixin foundation-callout {
+ .callout {
+ @include callout;
+
+ @each $name, $color in $foundation-palette {
+ &.#{$name} {
+ @include callout-style($color);
+ }
+ }
+
+ @each $size, $padding in map-remove($callout-sizes, default) {
+ &.#{$size} {
+ @include callout-size($padding);
+ }
+ }
+ }
+}
diff --git a/src/foundation/components/_card.scss b/src/foundation/components/_card.scss
new file mode 100644
index 0000000..a5bc78c
--- /dev/null
+++ b/src/foundation/components/_card.scss
@@ -0,0 +1,129 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group card
+////
+
+/// Default background color.
+/// @type Color
+$card-background: $white !default;
+
+/// Default font color for cards.
+/// @type Color
+$card-font-color: $body-font-color !default;
+
+/// Default background.
+/// @type Color
+$card-divider-background: $light-gray !default;
+
+/// Default border style.
+/// @type List
+$card-border: 1px solid $light-gray !default;
+
+/// Default card shadow.
+/// @type List
+$card-shadow: none !default;
+
+/// Default border radius.
+/// @type List
+$card-border-radius: $global-radius !default;
+
+/// Default padding.
+/// @type Number
+$card-padding: $global-padding !default;
+
+/// Default bottom margin.
+/// @type number
+$card-margin-bottom: $global-margin !default;
+
+/// Adds styles for a card container.
+/// @param {Color} $background - Background color of the card.
+/// @param {Color} $color - font color of the card.
+/// @param {Number} $margin - Bottom margin of the card.
+/// @param {List} $border - Border around the card.
+/// @param {List} $radius - border radius of the card.
+/// @param {List} $shadow - box shadow of the card.
+@mixin card-container(
+ $background: $card-background,
+ $color: $card-font-color,
+ $margin: $card-margin-bottom,
+ $border: $card-border,
+ $radius: $card-border-radius,
+ $shadow: $card-shadow
+) {
+ @if $global-flexbox {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ }
+
+ margin-bottom: $margin;
+
+ border: $border;
+ border-radius: $radius;
+
+ background: $background;
+ box-shadow: $shadow;
+
+ overflow: hidden;
+ color: $color;
+
+ & > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+/// Adds styles for a card divider.
+@mixin card-divider(
+ $background: $card-divider-background,
+ $padding: $card-padding
+) {
+ @if $global-flexbox {
+ display: flex;
+ flex: 0 1 auto;
+ }
+
+ padding: $padding;
+ background: $background;
+
+ & > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+/// Adds styles for a card section.
+@mixin card-section(
+ $padding: $card-padding
+) {
+ @if $global-flexbox {
+ flex: 1 0 auto;
+ }
+
+ padding: $padding;
+
+ & > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+@mixin foundation-card {
+ .card {
+ @include card-container;
+ }
+
+ .card-divider {
+ @include card-divider;
+ }
+
+ .card-section {
+ @include card-section;
+ }
+
+ // For IE 11 - Flexbug
+ // https://github.com/philipwalton/flexbugs/issues/75
+ .card-image {
+ min-height: 1px;
+ }
+}
diff --git a/src/foundation/components/_close-button.scss b/src/foundation/components/_close-button.scss
new file mode 100644
index 0000000..abb2c79
--- /dev/null
+++ b/src/foundation/components/_close-button.scss
@@ -0,0 +1,127 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group close-button
+////
+
+/// Default position of the close button. The first value should be `right` or `left`, and the second value should be `top` or `bottom`.
+/// @type List
+$closebutton-position: right top !default;
+
+/// Default z-index for a close button.
+/// @type Number
+$closebutton-z-index: 10 !default;
+
+/// Button size to use as default
+/// @type String
+/// @see $closebutton-size
+/// @see $closebutton-offset-horizontal
+/// @see $closebutton-offset-vertical
+$closebutton-default-size: medium !default;
+
+/// Right (or left) offset(s) for a close button.
+/// @type Number|Map
+$closebutton-offset-horizontal: (
+ small: 0.66rem,
+ medium: 1rem,
+) !default;
+
+/// Top (or bottom) offset(s) for a close button.
+/// @type Number|Map
+$closebutton-offset-vertical: (
+ small: 0.33em,
+ medium: 0.5rem,
+) !default;
+
+/// Size(s) of the close button. Used to generate sizing modifiers.
+/// @type Number|Map
+$closebutton-size: (
+ small: 1.5em,
+ medium: 2em,
+) !default;
+
+/// The line-height of the close button. It affects the spacing of the element.
+/// @type Number
+$closebutton-lineheight: 1 !default;
+
+/// Default color of the close button.
+/// @type Color
+$closebutton-color: $dark-gray !default;
+
+/// Default color of the close button when being hovered on.
+/// @type Color
+$closebutton-color-hover: $black !default;
+
+
+/// Get the size and position for a close button. If the input value is a number, the number is returned. If the input value is a config map and the map has the key `$size`, the value is returned.
+///
+/// @param {Number|Map} $value - A number or map that represents the size or position value(s) of the close button.
+/// @param {Keyword} $size - The size of the close button to use.
+///
+/// @return {Number} The given number or the value found in the map.
+@function -zf-get-size-val($value, $size) {
+ // Check if the value is a number
+ @if type-of($value) == 'number' {
+ // If it is, just return the number
+ @return $value;
+ }
+
+ // Check if the size name exists in the value map
+ @else if map-has-key($value, $size) {
+ // If it does, return the value
+ @return map-get($value, $size);
+ }
+}
+
+/// Sets the size and position of a close button.
+/// @param {Keyword} $size [medium] - The size to use. Set to `small` to create a small close button. The 'medium' values defined in `$closebutton-*` variables will be used as the default size and position of the close button.
+@mixin close-button-size($size) {
+ $x: nth($closebutton-position, 1);
+ $y: nth($closebutton-position, 2);
+
+ #{$x}: -zf-get-size-val($closebutton-offset-horizontal, $size);
+ #{$y}: -zf-get-size-val($closebutton-offset-vertical, $size);
+ font-size: -zf-get-size-val($closebutton-size, $size);
+ line-height: -zf-get-size-val($closebutton-lineheight, $size);
+}
+
+/// Adds styles for a close button, using the styles in the settings variables.
+@mixin close-button {
+ $x: nth($closebutton-position, 1);
+ $y: nth($closebutton-position, 2);
+
+ @include disable-mouse-outline;
+ position: absolute;
+ z-index: $closebutton-z-index;
+ color: $closebutton-color;
+ cursor: pointer;
+
+ &:hover,
+ &:focus {
+ color: $closebutton-color-hover;
+ }
+}
+
+@mixin foundation-close-button {
+ .close-button {
+ @include close-button;
+
+ // Generate a placeholder and a class for each size
+ @each $name, $size in $closebutton-size {
+ @at-root {
+ %zf-close-button--#{$name} {
+ @include close-button-size($name);
+ }
+ }
+
+ &.#{$name} {
+ @extend %zf-close-button--#{$name};
+ }
+ }
+
+ // Use by default the placeholder of the default size
+ @extend %zf-close-button--#{$closebutton-default-size};
+ }
+}
diff --git a/src/foundation/components/_drilldown.scss b/src/foundation/components/_drilldown.scss
new file mode 100644
index 0000000..81d3993
--- /dev/null
+++ b/src/foundation/components/_drilldown.scss
@@ -0,0 +1,140 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group drilldown
+////
+
+/// Transition property to use for animating menus.
+/// @type Transition
+$drilldown-transition: transform 0.15s linear !default;
+
+/// Adds arrows to drilldown items with submenus, as well as the back button.
+/// @type Boolean
+$drilldown-arrows: true !default;
+
+/// Sets drilldown menu item padding.
+/// @type Number
+$drilldown-padding: $global-menu-padding !default;
+
+/// Sets drilldown menu nested margin
+/// @type Number
+$drilldown-nested-margin: 0 !default;
+
+/// Background color for drilldown top level items.
+/// @type Color
+$drilldown-background: $white !default;
+
+/// Sets drilldown menu item padding in the submenu.
+/// @type Number
+$drilldown-submenu-padding: $drilldown-padding !default;
+
+/// Background color for drilldown submenus.
+/// @type Color
+$drilldown-submenu-background: $white !default;
+
+/// Sets drilldown arrow color if arrow is used.
+/// @type Color
+$drilldown-arrow-color: $primary-color !default;
+
+/// Sets drilldown arrow size if arrow is used.
+/// @type Length
+$drilldown-arrow-size: 6px !default;
+
+@mixin zf-drilldown-left-right-arrows {
+ .is-drilldown-submenu-parent > a {
+ position: relative;
+
+ &::after {
+ @include css-triangle($drilldown-arrow-size, $drilldown-arrow-color, $global-right);
+ position: absolute;
+ top: 50%;
+ margin-top: -1 * $drilldown-arrow-size;
+ #{$global-right}: 1rem;
+ }
+ }
+
+ &.align-left .is-drilldown-submenu-parent > a::after {
+ @include css-triangle($dropdownmenu-arrow-size, $dropdownmenu-arrow-color, right);
+ right: 1rem;
+ left: auto;
+ }
+
+ &.align-right .is-drilldown-submenu-parent > a::after {
+ @include css-triangle($dropdownmenu-arrow-size, $dropdownmenu-arrow-color, left);
+ right: auto;
+ left: 1rem;
+ }
+
+}
+
+@mixin foundation-drilldown-menu {
+ // Applied to the Menu container
+ .is-drilldown {
+ position: relative;
+ overflow: hidden;
+
+ li {
+ display: block;
+ }
+
+ &.animate-height {
+ transition: height 0.5s;
+ }
+ }
+
+ // The top level <ul>
+ .drilldown {
+ a {
+ padding: $drilldown-padding;
+ background: $drilldown-background;
+ }
+
+ // Applied to submenu <ul>s
+ .is-drilldown-submenu {
+ position: absolute;
+ top: 0;
+ #{$global-left}: 100%;
+ z-index: -1;
+
+ width: 100%;
+ background: $drilldown-submenu-background;
+ transition: $drilldown-transition;
+
+ &.is-active {
+ z-index: 1;
+ display: block;
+ transform: translateX(if($global-text-direction == ltr, -100%, 100%));
+ }
+
+ &.is-closing {
+ transform: translateX(if($global-text-direction == ltr, 100%, -100%));
+ }
+
+ // Submenu item padding
+ a {
+ padding: $drilldown-submenu-padding;
+ }
+ }
+
+ .nested.is-drilldown-submenu {
+ @include menu-nested($drilldown-nested-margin);
+ }
+
+ .drilldown-submenu-cover-previous {
+ min-height: 100%;
+ }
+
+ @if $drilldown-arrows {
+ @include zf-drilldown-left-right-arrows;
+
+ .js-drilldown-back > a::before {
+ @include css-triangle($drilldown-arrow-size, $drilldown-arrow-color, $global-left);
+ display: inline-block;
+ vertical-align: middle;
+ margin-#{$global-right}: 0.75rem; // Creates space between the arrow and the text
+ }
+ }
+ }
+}
diff --git a/src/foundation/components/_dropdown-menu.scss b/src/foundation/components/_dropdown-menu.scss
new file mode 100644
index 0000000..d2d3480
--- /dev/null
+++ b/src/foundation/components/_dropdown-menu.scss
@@ -0,0 +1,279 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group dropdown-menu
+////
+
+/// Enables arrows for items with dropdown menus.
+/// @type Boolean
+$dropdownmenu-arrows: true !default;
+
+/// Sets dropdown menu arrow color if arrow is used.
+/// @type Color
+$dropdownmenu-arrow-color: $anchor-color !default;
+
+/// Sets dropdown menu arrow size if arrow is used.
+/// @type Length
+$dropdownmenu-arrow-size: 6px !default;
+
+/// Sets dropdown menu arrow padding for aligning the arrow correctly.
+/// @type Length
+$dropdownmenu-arrow-padding: 1.5rem !default;
+
+/// Minimum width of dropdown sub-menus.
+/// @type Length
+$dropdownmenu-min-width: 200px !default;
+
+/// Background color for top level items.
+/// @type Color
+$dropdownmenu-background: null !default;
+
+/// Background color for dropdowns.
+/// @type Color
+$dropdownmenu-submenu-background: $white !default;
+
+/// Padding for top level items.
+/// @type Number
+$dropdownmenu-padding: $global-menu-padding !default;
+
+/// Sets dropdown menu nested margin
+/// @type Number
+$dropdownmenu-nested-margin: 0 !default;
+
+/// Padding for sub-menu items.
+/// @type Number
+$dropdownmenu-submenu-padding: $dropdownmenu-padding !default;
+
+/// Border for dropdown sub-menus.
+/// @type List
+$dropdownmenu-border: 1px solid $medium-gray !default;
+
+// Border width for dropdown sub-menus.
+// Used to adjust top margin of a sub-menu if a border is used.
+// @type Length
+$dropdownmenu-border-width: nth($dropdownmenu-border, 1);
+
+/// Text color of an active dropdown menu item. Explicit override for menu defaults
+/// @type Color
+$dropdown-menu-item-color-active: get-color(primary) !default;
+
+/// Background color of an active dropdown menu item. Explicit override for menu defaults
+/// @type Color
+$dropdown-menu-item-background-active: transparent !default;
+
+@mixin zf-dropdown-left-right-arrows {
+ > a::after {
+ #{$global-right}: 14px;
+ }
+
+ &.opens-left > a::after {
+ @include css-triangle($dropdownmenu-arrow-size, $dropdownmenu-arrow-color, left);
+ right: auto;
+ left: 5px;
+ }
+
+ &.opens-right > a::after {
+ @include css-triangle($dropdownmenu-arrow-size, $dropdownmenu-arrow-color, right);
+ }
+}
+
+@mixin dropdown-menu-direction($dir: horizontal) {
+ @if $dir == horizontal {
+ > li.opens-left { // sass-lint:disable-line no-qualifying-elements
+ > .is-dropdown-submenu {
+ top: 100%;
+ right: 0;
+ left: auto;
+ }
+ }
+
+ > li.opens-right { // sass-lint:disable-line no-qualifying-elements
+ > .is-dropdown-submenu {
+ top: 100%;
+ right: auto;
+ left: 0;
+ }
+ }
+
+ @if $dropdownmenu-arrows {
+ > li.is-dropdown-submenu-parent > a { // sass-lint:disable-line no-qualifying-elements
+ position: relative;
+ padding-#{$global-right}: $dropdownmenu-arrow-padding;
+ }
+
+ > li.is-dropdown-submenu-parent > a::after { // sass-lint:disable-line no-qualifying-elements
+ @include css-triangle($dropdownmenu-arrow-size, $dropdownmenu-arrow-color, down);
+ #{$global-right}: 5px;
+ #{$global-left}: auto;
+ margin-top: -1 * ($dropdownmenu-arrow-size / 2);
+ }
+ }
+ }
+ @else if $dir == vertical {
+ > li {
+ .is-dropdown-submenu {
+ top: 0;
+ }
+
+ &.opens-left {
+ > .is-dropdown-submenu {
+ top: 0;
+ right: 100%;
+ left: auto;
+ }
+ }
+
+ &.opens-right {
+ > .is-dropdown-submenu {
+ right: auto;
+ left: 100%;
+ }
+ }
+
+ @if $dropdownmenu-arrows {
+ @include zf-dropdown-left-right-arrows;
+ }
+ }
+ }
+ @else {
+ @warn 'The direction used for dropdown-menu-direction() must be horizontal or vertical.';
+ }
+}
+
+@mixin foundation-dropdown-menu {
+ .dropdown.menu {
+ @include dropdown-menu-direction(horizontal);
+
+ a {
+ @include disable-mouse-outline;
+ }
+
+ // Top-level item
+ > li > a {
+ background: $dropdownmenu-background;
+ padding: $dropdownmenu-padding;
+ }
+
+ // Top-level item active state
+ > li.is-active > a {
+ background: $dropdown-menu-item-background-active;
+ color: $dropdown-menu-item-color-active;
+ }
+
+ .no-js & ul {
+ display: none;
+ }
+
+ .nested.is-dropdown-submenu {
+ @include menu-nested($dropdownmenu-nested-margin);
+ }
+
+ &.vertical {
+ @include dropdown-menu-direction(vertical);
+ }
+
+ @each $size in $breakpoint-classes {
+ @if $size != $-zf-zero-breakpoint {
+ @include breakpoint($size) {
+ &.#{$size}-horizontal {
+ @include dropdown-menu-direction(horizontal);
+ }
+
+ &.#{$size}-vertical {
+ @include dropdown-menu-direction(vertical);
+ }
+ }
+ }
+ }
+
+ &.align-right {
+ .is-dropdown-submenu.first-sub {
+ top: 100%;
+ right: 0;
+ left: auto;
+ }
+ }
+ }
+
+ .is-dropdown-menu.vertical {
+ width: 100px;
+
+ &.align-right {
+ float: right;
+ }
+ }
+
+ .is-dropdown-submenu-parent {
+ position: relative;
+
+ a::after {
+ position: absolute;
+ top: 50%;
+ #{$global-right}: 5px;
+ #{$global-left}: auto;
+ margin-top: -1 * $dropdownmenu-arrow-size;
+ }
+
+ &.opens-inner > .is-dropdown-submenu {
+
+ top: 100%;
+ @if $global-text-direction == 'rtl' {
+ right: auto;
+ }
+ @else {
+ left: auto;
+ }
+ }
+
+ &.opens-left > .is-dropdown-submenu {
+ right: 100%;
+ left: auto;
+ }
+
+ &.opens-right > .is-dropdown-submenu {
+ right: auto;
+ left: 100%;
+ }
+ }
+
+ .is-dropdown-submenu {
+ position: absolute;
+ top: 0;
+ #{$global-left}: 100%;
+ z-index: 1;
+
+ display: none;
+ min-width: $dropdownmenu-min-width;
+
+ border: $dropdownmenu-border;
+ background: $dropdownmenu-submenu-background;
+
+ .dropdown & a {
+ padding: $dropdownmenu-submenu-padding;
+ }
+
+ .is-dropdown-submenu-parent {
+ @if $dropdownmenu-arrows {
+ @include zf-dropdown-left-right-arrows;
+ }
+ }
+
+ @if (type-of($dropdownmenu-border-width) == 'number') {
+ .is-dropdown-submenu {
+ margin-top: (-$dropdownmenu-border-width);
+ }
+ }
+
+ > li {
+ width: 100%;
+ }
+
+ // [TODO] Cut back specificity
+ //&:not(.js-dropdown-nohover) > .is-dropdown-submenu-parent:hover > &, // why is this line needed? Opening is handled by JS and this causes some ugly flickering when the sub is re-positioned automatically...
+ &.js-dropdown-active {
+ display: block;
+ }
+ }
+}
diff --git a/src/foundation/components/_dropdown.scss b/src/foundation/components/_dropdown.scss
new file mode 100644
index 0000000..53afead
--- /dev/null
+++ b/src/foundation/components/_dropdown.scss
@@ -0,0 +1,82 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group dropdown
+////
+
+/// Padding for dropdown panes.
+/// @type List
+$dropdown-padding: 1rem !default;
+
+/// Background for dropdown panes.
+/// @type Color
+$dropdown-background: $body-background !default;
+
+/// Border for dropdown panes.
+/// @type List
+$dropdown-border: 1px solid $medium-gray !default;
+
+/// Font size for dropdown panes.
+/// @type List
+$dropdown-font-size: 1rem !default;
+
+/// Width for dropdown panes.
+/// @type Number
+$dropdown-width: 300px !default;
+
+/// Border radius dropdown panes.
+/// @type Number
+$dropdown-radius: $global-radius !default;
+
+/// Sizes for dropdown panes. Each size is a CSS class you can apply.
+/// @type Map
+$dropdown-sizes: (
+ tiny: 100px,
+ small: 200px,
+ large: 400px,
+) !default;
+
+/// Applies styles for a basic dropdown.
+@mixin dropdown-container {
+ position: absolute;
+ z-index: 10;
+
+ display: none;
+
+ width: $dropdown-width;
+ padding: $dropdown-padding;
+
+ visibility: hidden;
+ border: $dropdown-border;
+ border-radius: $dropdown-radius;
+ background-color: $dropdown-background;
+
+ font-size: $dropdown-font-size;
+
+
+ // Allow an intermittent state to do positioning before making visible.
+ &.is-opening {
+ display: block;
+ }
+
+ &.is-open {
+ display: block;
+ visibility: visible;
+ }
+}
+
+@mixin foundation-dropdown {
+ .dropdown-pane {
+ @include dropdown-container;
+ }
+
+ @each $name, $size in $dropdown-sizes {
+ .dropdown-pane {
+ &.#{$name} {
+ width: $size;
+ }
+ }
+ }
+}
diff --git a/src/foundation/components/_flex-video.scss b/src/foundation/components/_flex-video.scss
new file mode 100644
index 0000000..bf85a6c
--- /dev/null
+++ b/src/foundation/components/_flex-video.scss
@@ -0,0 +1 @@
+@import 'responsive-embed';
diff --git a/src/foundation/components/_flex.scss b/src/foundation/components/_flex.scss
new file mode 100644
index 0000000..eed2eba
--- /dev/null
+++ b/src/foundation/components/_flex.scss
@@ -0,0 +1,119 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group Flexbox Utilities
+////
+//
+/// Default value for the count of source ordering`
+/// @type Number
+$flex-source-ordering-count: 6 !default;
+
+/// Quickly disable/enable Responsive breakpoints for Vanilla Flex Helpers.
+/// @type Boolean
+$flexbox-responsive-breakpoints: true !default;
+
+@mixin flex-helpers {
+ .flex-container {
+ @include flex;
+ }
+
+ .flex-child-auto {
+ flex: 1 1 auto;
+ }
+
+ .flex-child-grow {
+ flex: 1 0 auto;
+ }
+
+ .flex-child-shrink {
+ flex: 0 1 auto;
+ }
+
+ @each $dir, $prop in $-zf-flex-direction {
+ .flex-dir-#{$dir} {
+ @include flex-direction($prop);
+ }
+ }
+
+ @if ($flexbox-responsive-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-flex-container {
+ @include flex;
+ }
+
+ .#{$size}-flex-child-auto {
+ flex: 1 1 auto;
+ }
+
+ .#{$size}-flex-child-grow {
+ flex: 1 0 auto;
+ }
+
+ .#{$size}-flex-child-shrink {
+ flex: 0 1 auto;
+ }
+
+ @each $dir, $prop in $-zf-flex-direction {
+ .#{$size}-flex-dir-#{$dir} {
+ @include flex-direction($prop);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@mixin foundation-flex-classes {
+ // Horizontal alignment using justify-content
+ @each $hdir, $prop in $-zf-flex-justify {
+ .align-#{$hdir} {
+ @include flex-align($x: $hdir);
+ }
+ }
+
+ // Horizontal alignment Specifically for Vertical Menu
+ @each $hdir, $prop in map-remove($-zf-flex-justify, 'justify', 'spaced') {
+ .align-#{$hdir} {
+ &.vertical {
+ &.menu > li > a {
+ @include flex-align($x: $hdir);
+ }
+ }
+ }
+ }
+
+ // Vertical alignment using align-items and align-self
+ @each $vdir, $prop in $-zf-flex-align {
+ .align-#{$vdir} {
+ @include flex-align($y: $vdir);
+ }
+
+ .align-self-#{$vdir} {
+ @include flex-align-self($y: $vdir);
+ }
+ }
+
+ // Central alignment of content
+ .align-center-middle {
+ @include flex-align($x: center, $y: middle);
+ align-content: center;
+ }
+
+ // Source ordering
+ @include -zf-each-breakpoint {
+ @for $i from 1 through $flex-source-ordering-count {
+ .#{$-zf-size}-order-#{$i} {
+ @include flex-order($i);
+ }
+ }
+ }
+
+ // Vanilla Flexbox Helpers
+ @include flex-helpers;
+}
diff --git a/src/foundation/components/_float.scss b/src/foundation/components/_float.scss
new file mode 100644
index 0000000..3f81f3d
--- /dev/null
+++ b/src/foundation/components/_float.scss
@@ -0,0 +1,27 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group float
+////
+
+@mixin foundation-float-classes {
+ .float-left {
+ float: left !important;
+ }
+
+ .float-right {
+ float: right !important;
+ }
+
+ .float-center {
+ display: block;
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ .clearfix {
+ @include clearfix;
+ }
+}
diff --git a/src/foundation/components/_label.scss b/src/foundation/components/_label.scss
new file mode 100644
index 0000000..cf7cf7d
--- /dev/null
+++ b/src/foundation/components/_label.scss
@@ -0,0 +1,64 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group label
+////
+
+/// Default background color for labels.
+/// @type Color
+$label-background: $primary-color !default;
+
+/// Default text color for labels.
+/// @type Color
+$label-color: $white !default;
+
+/// Alternate text color for labels.
+/// @type Color
+$label-color-alt: $black !default;
+
+/// Coloring classes. A map of classes to output in your CSS, like `.secondary`, `.success`, and so on.
+/// @type Map
+$label-palette: $foundation-palette !default;
+
+/// Default font size for labels.
+/// @type Number
+$label-font-size: 0.8rem !default;
+
+/// Default padding inside labels.
+/// @type Number
+$label-padding: 0.33333rem 0.5rem !default;
+
+/// Default radius of labels.
+/// @type Number
+$label-radius: $global-radius !default;
+
+/// Generates base styles for a label.
+@mixin label {
+ display: inline-block;
+ padding: $label-padding;
+
+ border-radius: $label-radius;
+
+ font-size: $label-font-size;
+ line-height: 1;
+ white-space: nowrap;
+ cursor: default;
+}
+
+@mixin foundation-label {
+ .label {
+ @include label;
+
+ background: $label-background;
+ color: $label-color;
+
+ @each $name, $color in $label-palette {
+ &.#{$name} {
+ background: $color;
+ color: color-pick-contrast($color, ($label-color, $label-color-alt));
+ }
+ }
+ }
+}
diff --git a/src/foundation/components/_media-object.scss b/src/foundation/components/_media-object.scss
new file mode 100644
index 0000000..0ae8c51
--- /dev/null
+++ b/src/foundation/components/_media-object.scss
@@ -0,0 +1,114 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group media-object
+////
+
+/// Bottom margin of a media object.
+/// @type Number
+$mediaobject-margin-bottom: $global-margin !default;
+
+/// Left and right padding on sections within a media object.
+/// @type Number
+$mediaobject-section-padding: $global-padding !default;
+
+/// Width of images within a media object, when the object is stacked vertically. Set to 'auto' to use the image's natural width.
+/// @type Number
+$mediaobject-image-width-stacked: 100% !default;
+
+/// Adds styles for a media object container.
+@mixin media-object-container {
+ display: if($global-flexbox, flex, block);
+ margin-bottom: $mediaobject-margin-bottom;
+
+ @if $global-flexbox {
+ flex-wrap: nowrap;
+ }
+
+ img {
+ max-width: none;
+ }
+
+ @if $global-flexbox {
+ &.stack-for-#{$-zf-zero-breakpoint} {
+ @include breakpoint($-zf-zero-breakpoint only) {
+ flex-wrap: wrap;
+ }
+ }
+ }
+}
+
+/// Adds styles for sections within a media object.
+/// @param {Number} $padding [$mediaobject-section-padding] - Padding between sections.
+@mixin media-object-section($padding: $mediaobject-section-padding) {
+ @if $global-flexbox {
+ flex: 0 1 auto;
+ }
+ @else {
+ display: table-cell;
+ vertical-align: top;
+ }
+
+ &:first-child {
+ padding-#{$global-right}: $padding;
+ }
+
+ &:last-child:not(:nth-child(2)) {
+ padding-#{$global-left}: $padding;
+ }
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+
+ .stack-for-#{$-zf-zero-breakpoint} & {
+ @include breakpoint($-zf-zero-breakpoint only) {
+ @include media-object-stack;
+ }
+ }
+
+ @if $global-flexbox {
+ &.main-section {
+ flex: 1 1 0px; // sass-lint:disable-line zero-unit
+ }
+ }
+ @else {
+ &.middle {
+ vertical-align: middle;
+ }
+
+ &.bottom {
+ vertical-align: bottom;
+ }
+ }
+}
+
+/// Adds styles to stack sections of a media object. Apply this to the section elements, not the container.
+@mixin media-object-stack {
+ padding: 0;
+ padding-bottom: $mediaobject-section-padding;
+
+ @if $global-flexbox {
+ flex-basis: 100%;
+ max-width: 100%;
+ }
+ @else {
+ display: block;
+ }
+
+ img {
+ width: $mediaobject-image-width-stacked;
+ }
+}
+
+@mixin foundation-media-object {
+ .media-object {
+ @include media-object-container;
+ }
+
+ .media-object-section {
+ @include media-object-section;
+ }
+}
diff --git a/src/foundation/components/_menu-icon.scss b/src/foundation/components/_menu-icon.scss
new file mode 100644
index 0000000..b0df173
--- /dev/null
+++ b/src/foundation/components/_menu-icon.scss
@@ -0,0 +1,9 @@
+@mixin foundation-menu-icon {
+ .menu-icon {
+ @include hamburger($color: $titlebar-icon-color, $color-hover: $titlebar-icon-color-hover);
+ }
+
+ .menu-icon.dark {
+ @include hamburger;
+ }
+}
diff --git a/src/foundation/components/_menu.scss b/src/foundation/components/_menu.scss
new file mode 100644
index 0000000..0cbb9d0
--- /dev/null
+++ b/src/foundation/components/_menu.scss
@@ -0,0 +1,495 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group menu
+////
+
+/// Margin of a menu.
+/// @type Number
+$menu-margin: 0 !default;
+
+/// Left-hand margin of a nested menu.
+/// @type Number
+$menu-nested-margin: $global-menu-nested-margin !default;
+
+/// Padding for items in a pill menu.
+/// @type Number
+$menu-items-padding: $global-menu-padding !default;
+
+/// margin for items in a simple menu.
+/// @type Number
+$menu-simple-margin: 1rem !default;
+
+/// Text color of an active menu item.
+/// @type Color
+$menu-item-color-active: $white !default;
+
+/// Alternative text color of an active menu item..
+/// @type Color
+$menu-item-color-alt-active: $black !default;
+
+/// Background color of an active menu item.
+/// @type Color
+$menu-item-background-active: get-color(primary) !default;
+
+/// Spacing between an icon and text in a menu item.
+/// @type Number
+$menu-icon-spacing: 0.25rem !default;
+
+/// Backward compatibility for menu state. If true, this duplicate `active` with `is-active`.
+/// But please note that `active` will be removed in upcoming versions.
+/// @type Boolean
+$menu-state-back-compat: true !default;
+
+/// Backward compatibility for menu centered. If true, this duplicate `.menu-centered > .menu` with `.menu.align-center`.
+/// But please note that `menu-centered` will be removed in upcoming versions.
+/// @type Boolean
+$menu-centered-back-compat: true !default;
+
+/// Backward compatibility for using `icon-*` classes without `.icons` classes
+/// But please note that this backward compatibility will be removed in upcoming versions.
+/// @type Boolean
+$menu-icons-back-compat: true !default;
+
+/// Creates the base styles for a Menu.
+@mixin menu-base {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ position: relative;
+
+ @if $global-flexbox {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ li {
+ @include disable-mouse-outline;
+ }
+
+ a,
+ .button {
+ line-height: 1;
+ text-decoration: none;
+ display: block;
+ padding: $menu-items-padding;
+ }
+
+ // Reset styles of inner elements
+ input,
+ select,
+ a,
+ button {
+ margin-bottom: 0;
+ }
+
+ input {
+ display: inline-block;
+ }
+}
+
+/// Expands the items of a Menu, so each item is the same width.
+@mixin menu-expand {
+ @if $global-flexbox {
+ li {
+ flex: 1 1 0px; // sass-lint:disable-line zero-unit
+ }
+ }
+ @else {
+ display: table;
+ width: 100%;
+
+ > li {
+ display: table-cell;
+ vertical-align: middle;
+ }
+ }
+}
+
+/// Align menu items.
+@mixin menu-align($alignment) {
+ @if $alignment == left {
+ @if $global-flexbox {
+ justify-content: flex-start;
+ }
+ @else {
+ text-align: $global-left;
+ }
+ }
+ @else if $alignment == right {
+ @if $global-flexbox {
+ li {
+ display: flex;
+ justify-content: flex-end;
+
+ .submenu li {
+ justify-content: flex-start;
+ }
+ }
+
+ &.vertical li {
+ display: block;
+ text-align: $global-right;
+
+ .submenu li {
+ text-align: $global-right;
+ }
+ }
+ }
+ @else {
+ text-align: $global-right;
+
+ .submenu li {
+ text-align: $global-left;
+ }
+
+ &.vertical {
+ .submenu li {
+ text-align: $global-right;
+ }
+ }
+ }
+ }
+ @else if $alignment == center {
+ @if $global-flexbox {
+ li {
+ display: flex;
+ justify-content: center;
+
+ .submenu li {
+ justify-content: flex-start;
+ }
+ }
+ }
+ @else {
+ text-align: center;
+
+ .submenu li {
+ text-align: $global-left;
+ }
+ }
+ }
+}
+
+/// Sets the direction of a Menu.
+/// @param {Keyword} $dir [horizontal] - Direction of the Menu. Can be `horizontal` or `vertical`.
+@mixin menu-direction($dir: horizontal) {
+ @if $dir == horizontal {
+ @if $global-flexbox {
+ flex-wrap: wrap;
+ flex-direction: row;
+ }
+ @else {
+ li {
+ display: inline-block;
+ }
+ }
+ }
+ @else if $dir == vertical {
+ @if $global-flexbox {
+ flex-wrap: nowrap;
+ flex-direction: column;
+ }
+ @else {
+ li {
+ display: block;
+ }
+ }
+ }
+ @else {
+ @warn 'The direction used for menu-direction() must be horizontal or vertical.';
+ }
+}
+
+/// Creates a simple Menu, which has no padding or hover state.
+/// @param {Keyword} $dir [$global-left] - Direction of the menu. This effects the side of the `<li>` that receives the margin.
+/// @param {Number} $margin [$menu-simple-margin] - The margin to apply to each `<li>`.
+@mixin menu-simple($dir: $global-left, $margin: $menu-simple-margin) {
+ @if $global-flexbox {
+ align-items: center;
+ }
+
+ li + li {
+ margin-#{$dir}: $margin;
+ }
+
+ a {
+ padding: 0;
+ }
+}
+
+/// Adds styles for a nested Menu, by adding `margin-left` to the menu.
+/// @param {Keyword|Number} $margin [$menu-nested-margin] - Length of the margin.
+/// @param {Keyword} $nested-alignment [left] - Alignment of the nested class
+@mixin menu-nested(
+ $margin: $menu-nested-margin,
+ $nested-alignment: left
+) {
+ @if $nested-alignment == right {
+ margin-#{$global-right}: $margin;
+ margin-#{$global-left}: 0;
+ }
+ @else {
+ margin-#{$global-right}: 0;
+ margin-#{$global-left}: $margin;
+ }
+
+}
+
+/// Adds basic styles for icons in menus.
+@mixin menu-icons() {
+ @if $global-flexbox {
+ a {
+ display: flex;
+ }
+ }
+ @else {
+ img,
+ i,
+ svg {
+ vertical-align: middle;
+
+ + span {
+ vertical-align: middle;
+ }
+ }
+ }
+}
+
+/// Adds position classes for icons within a menu.
+@mixin menu-icon-position($position: left, $spacing: $menu-icon-spacing) {
+ @if $position == left {
+ li a {
+ @if $global-flexbox {
+ flex-flow: row nowrap;
+ }
+
+ img,
+ i,
+ svg {
+ margin-#{$global-right}: $spacing;
+
+ @if not $global-flexbox {
+ display: inline-block;
+ }
+ }
+ }
+ }
+ @else if $position == right {
+ li a {
+ @if $global-flexbox {
+ flex-flow: row nowrap;
+ }
+
+ img,
+ i,
+ svg {
+ margin-#{$global-left}: $spacing;
+
+ @if not $global-flexbox {
+ display: inline-block;
+ }
+ }
+ }
+ }
+ @else if $position == top {
+ li a {
+ @if $global-flexbox {
+ flex-flow: column nowrap;
+ }
+ @else {
+ text-align: center;
+ }
+
+ img,
+ i,
+ svg {
+ @if not $global-flexbox {
+ display: block;
+ margin: 0 auto $spacing;
+ }
+ @else {
+ align-self: stretch;
+ margin-bottom: $spacing;
+ text-align: center;
+ }
+ }
+ }
+ }
+ @else if $position == bottom {
+ li a {
+ @if $global-flexbox {
+ flex-flow: column nowrap;
+ }
+ @else {
+ text-align: center;
+ }
+
+ img,
+ i,
+ svg {
+ @if not $global-flexbox {
+ display: block;
+ margin: $spacing auto 0;
+ }
+ @else {
+ align-self: stretch;
+ margin-bottom: $spacing;
+ text-align: center;
+ }
+ }
+ }
+ }
+}
+
+@mixin menu-text {
+ padding: $global-menu-padding;
+
+ font-weight: bold;
+ line-height: 1;
+ color: inherit;
+}
+
+@mixin menu-state-active {
+ background: $menu-item-background-active;
+ color: color-pick-contrast($menu-item-background-active, ($menu-item-color-active, $menu-item-color-alt-active));
+}
+
+@mixin foundation-menu {
+ .menu {
+ @include menu-base;
+
+ // Default orientation: horizontal
+ &, &.horizontal {
+ @include menu-direction(horizontal);
+ }
+
+ // Vertical orientation modifier
+ &.vertical {
+ @include menu-direction(vertical);
+ }
+
+ // Even-width modifier for horizontal orientation
+ &.expanded {
+ @include menu-expand;
+ }
+
+ // Simple
+ &.simple {
+ @include menu-simple;
+ }
+
+ // Breakpoint specific versions
+ @include -zf-each-breakpoint($small: false) {
+ &.#{$-zf-size}-horizontal {
+ @include menu-direction(horizontal);
+ }
+
+ &.#{$-zf-size}-vertical {
+ @include menu-direction(vertical);
+ }
+
+ &.#{$-zf-size}-expanded {
+ @include menu-expand;
+ }
+
+ &.#{$-zf-size}-simple {
+ @include menu-expand;
+ }
+ }
+
+ // Nesting
+ &.nested {
+ @include menu-nested;
+ }
+
+ // Icon Base Styles
+ &.icons {
+ @include menu-icons;
+ }
+
+ // Backward Compatibility for active state
+ @if $menu-icons-back-compat {
+ &.icon-top,
+ &.icon-right,
+ &.icon-bottom,
+ &.icon-left {
+ @include menu-icons;
+ }
+ }
+
+ // Icon Left
+ &.icon-left {
+ @include menu-icon-position(left);
+ }
+
+ // Icon Right
+ &.icon-right {
+ @include menu-icon-position(right);
+ }
+
+ // Icon Top
+ &.icon-top {
+ @include menu-icon-position(top);
+ }
+
+ // Icon Bottom
+ &.icon-bottom {
+ @include menu-icon-position(bottom);
+ }
+
+ // Active state
+ .is-active > a {
+ @include menu-state-active;
+ }
+
+ // Backward Compatibility for active state
+ @if $menu-state-back-compat {
+ .active > a {
+ @include menu-state-active;
+ }
+ }
+
+ // Align left
+ &.align-#{$global-left} {
+ @include menu-align(left);
+ }
+
+ // Align right
+ &.align-#{$global-right} {
+ @include menu-align(right);
+
+ .nested {
+ @include menu-nested($nested-alignment: right);
+ }
+ }
+
+ // Align center
+ &.align-center {
+ @include menu-align(center);
+ }
+
+ .menu-text {
+ @include menu-text;
+ }
+ }
+
+ @if $menu-centered-back-compat {
+ .menu-centered {
+ > .menu {
+ @if $global-flexbox {
+ justify-content: center;
+ }
+
+ @include menu-align(center);
+ }
+ }
+ }
+
+ // Prevent FOUC when using the Responsive Menu plugin
+ .no-js [data-responsive-menu] ul {
+ display: none;
+ }
+}
diff --git a/src/foundation/components/_off-canvas.scss b/src/foundation/components/_off-canvas.scss
new file mode 100644
index 0000000..29eef9d
--- /dev/null
+++ b/src/foundation/components/_off-canvas.scss
@@ -0,0 +1,511 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group off-canvas
+////
+
+/// Width map of a left/right off-canvas panel.
+/// @type Map
+$offcanvas-sizes: (
+ small: 250px,
+) !default;
+
+/// Height map of a top/bottom off-canvas panel.
+/// @type Map
+$offcanvas-vertical-sizes: (
+ small: 250px,
+) !default;
+
+/// Background color of an off-canvas panel.
+/// @type Color
+$offcanvas-background: $light-gray !default;
+
+/// Box shadow for the off-canvas overlap panel.
+/// @type Shadow
+$offcanvas-shadow: 0 0 10px rgba($black, 0.7) !default;
+
+/// Inner box shadow size for the off-canvas push panel.
+/// @type Number
+$offcanvas-inner-shadow-size: 20px !default;
+
+/// Inner box shadow color for the off-canvas push panel.
+/// @type Color
+$offcanvas-inner-shadow-color: rgba($black, 0.25) !default;
+
+/// Z-index of an off-canvas content overlay.
+/// @type Number
+$offcanvas-overlay-zindex: 11 !default;
+
+/// Z-index of an off-canvas panel with the `push` transition.
+/// @type Number
+$offcanvas-push-zindex: 12 !default;
+
+/// Z-index of an off-canvas panel with the `overlap` transition.
+/// @type Number
+$offcanvas-overlap-zindex: 13 !default;
+
+/// Z-index of an off-canvas panel using the `reveal-for-*` classes or mixin.
+/// @type Number
+$offcanvas-reveal-zindex: 12 !default;
+
+/// Length of the animation on an off-canvas panel.
+/// @type Number
+$offcanvas-transition-length: 0.5s !default;
+
+/// Timing function of the animation on an off-canvas panel.
+/// @type Keyword
+$offcanvas-transition-timing: ease !default;
+
+/// If `true`, a revealed off-canvas will be fixed-position, and scroll with the screen.
+/// @type Bool
+$offcanvas-fixed-reveal: true !default;
+
+/// Background color for the overlay that appears when an off-canvas panel is open.
+/// @type Color
+$offcanvas-exit-background: rgba($white, 0.25) !default;
+
+/// CSS class used for the main content area. The off-canvas mixins use this to target the page content.
+$maincontent-class: 'off-canvas-content' !default;
+
+/// Adds baseline styles for off-canvas. This CSS is required to make the other pieces work.
+@mixin off-canvas-basics {
+
+ /// Transform deprecated size settings into map & show warning
+ @if variable-exists(offcanvas-size) {
+ $offcanvas-sizes: (small: $offcanvas-size, medium: $offcanvas-size) !global;
+ @warn '$offcanvas-size is deprecated and not used anymore! Please update your settings and use the map $offcanvas-sizes instead';
+ }
+ @if variable-exists(offcanvas-vertical-size) {
+ $offcanvas-vertical-sizes: (small: $offcanvas-vertical-size, medium: $offcanvas-vertical-size) !global;
+ @warn '$offcanvas-vertical-size is deprecated and not used anymore! Please update your settings and use the map $offcanvas-vertical-sizes instead';
+ }
+
+ // Checks the z-indexes and increase them due to backwards compatibility.
+ // This is necessary because the overlay's z-index is new since v6.4 and may be identical to the user custom settings of the push z-index.
+ @if $offcanvas-push-zindex <= $offcanvas-overlay-zindex { $offcanvas-push-zindex: $offcanvas-overlay-zindex + 1 !global; }
+ @if $offcanvas-overlap-zindex <= $offcanvas-push-zindex { $offcanvas-overlap-zindex: $offcanvas-push-zindex + 1 !global; }
+ @if $offcanvas-reveal-zindex <= $offcanvas-overlay-zindex { $offcanvas-reveal-zindex: $offcanvas-overlay-zindex + 1 !global; }
+
+ // Hides overflow on body when an off-canvas panel is open.
+ .is-off-canvas-open {
+ overflow: hidden;
+ }
+
+ // Off-canvas overlay (generated by JavaScript)
+ .js-off-canvas-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: $offcanvas-overlay-zindex;
+
+ width: 100%;
+ height: 100%;
+
+ transition: opacity $offcanvas-transition-length $offcanvas-transition-timing, visibility $offcanvas-transition-length $offcanvas-transition-timing;
+
+ background: $offcanvas-exit-background;
+
+ opacity: 0;
+ visibility: hidden;
+
+ overflow: hidden;
+
+ &.is-visible {
+ opacity: 1;
+ visibility: visible;
+ }
+
+ &.is-closable {
+ cursor: pointer;
+ }
+
+ &.is-overlay-absolute {
+ position: absolute;
+ }
+
+ &.is-overlay-fixed {
+ position: fixed;
+ }
+ }
+}
+
+// Adds basic styles for an off-canvas wrapper.
+@mixin off-canvas-wrapper() {
+ position: relative;
+ overflow: hidden;
+}
+
+/// Adds basic styles for an off-canvas panel.
+@mixin off-canvas-base(
+ $background: $offcanvas-background,
+ $transition: $offcanvas-transition-length $offcanvas-transition-timing,
+ $fixed: true
+) {
+ @include disable-mouse-outline;
+
+ @if $fixed == true {
+ position: fixed;
+ }
+ @else {
+ position: absolute;
+ }
+
+ // Set the off-canvas z-index.
+ z-index: $offcanvas-push-zindex;
+
+ // Increase CSS specificity
+ &.is-transition-push {
+ z-index: $offcanvas-push-zindex;
+ }
+
+ transition: transform $transition;
+ backface-visibility: hidden;
+
+ background: $background;
+
+ // Hide inactive off-canvas within the content that have the same position
+ &.is-closed {
+ visibility: hidden;
+ }
+
+ // Overlap only styles.
+ &.is-transition-overlap {
+ z-index: $offcanvas-overlap-zindex;
+
+ &.is-open {
+ box-shadow: $offcanvas-shadow;
+ }
+ }
+
+ // Sets transform to 0 to show an off-canvas panel.
+ &.is-open {
+ transform: translate(0, 0);
+ }
+}
+
+/// Adds styles to position an off-canvas panel to the left/right/top/bottom.
+@mixin off-canvas-position(
+ $position: left,
+ $orientation: horizontal,
+ $sizes: if($orientation == horizontal, $offcanvas-sizes, $offcanvas-vertical-sizes)
+) {
+ @if $position == left {
+ top: 0;
+ left: 0;
+ height: 100%;
+ overflow-y: auto;
+
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ width: $size;
+ transform: translateX(-$size);
+ }
+ }
+
+ // Sets the position for nested off-canvas element
+ @at-root .#{$maincontent-class} .off-canvas.position-#{$position} {
+
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ transform: translateX(-$size);
+ }
+ }
+ &.is-transition-overlap.is-open {
+ transform: translate(0, 0);
+ }
+ }
+
+ // Sets the open position for the content
+ @at-root .#{$maincontent-class}.is-open-#{$position} {
+ &.has-transition-push {
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ transform: translateX($size);
+ }
+ }
+ }
+ }
+ }
+ @else if $position == right {
+ top: 0;
+ right: 0;
+ height: 100%;
+ overflow-y: auto;
+
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ width: $size;
+ transform: translateX($size);
+ }
+ }
+
+ // Sets the position for nested off-canvas element
+ @at-root .#{$maincontent-class} .off-canvas.position-#{$position} {
+
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ transform: translateX($size);
+ }
+ }
+ &.is-transition-overlap.is-open {
+ transform: translate(0, 0);
+ }
+ }
+
+ // Sets the open position for the content
+ @at-root .#{$maincontent-class}.is-open-#{$position} {
+ &.has-transition-push {
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ transform: translateX(-$size);
+ }
+ }
+ }
+ }
+ }
+ @else if $position == top {
+ top: 0;
+ left: 0;
+ width: 100%;
+ overflow-x: auto;
+
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ height: $size;
+ transform: translateY(-$size);
+ }
+ }
+
+ // Sets the position for nested off-canvas element
+ @at-root .#{$maincontent-class} .off-canvas.position-#{$position} {
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ transform: translateY(-$size);
+ }
+ }
+ &.is-transition-overlap.is-open {
+ transform: translate(0, 0);
+ }
+ }
+
+ // Sets the open position for the content
+ @at-root .#{$maincontent-class}.is-open-#{$position} {
+ &.has-transition-push {
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ transform: translateY($size);
+ }
+ }
+ }
+ }
+ }
+ @else if $position == bottom {
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ overflow-x: auto;
+
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ height: $size;
+ transform: translateY($size);
+ }
+ }
+
+ // Sets the position for nested off-canvas element
+ @at-root .#{$maincontent-class} .off-canvas.position-#{$position} {
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ transform: translateY($size);
+ }
+ }
+ &.is-transition-overlap.is-open {
+ transform: translate(0, 0);
+ }
+ }
+
+ // Sets the open position for the content
+ @at-root .#{$maincontent-class}.is-open-#{$position} {
+ &.has-transition-push {
+ @each $name, $size in $sizes {
+ @include breakpoint($name) {
+ transform: translateY(-$size);
+ }
+ }
+ }
+ }
+ }
+
+ // If $offcanvas-inner-shadow-size is set, add inner box-shadow.
+ // This mimics the off-canvas panel having a lower z-index, without having to have one.
+ @if $offcanvas-inner-shadow-size {
+ &.is-transition-push {
+ @if $position == left {
+ @include inner-side-shadow(right, $offcanvas-inner-shadow-size, $offcanvas-inner-shadow-color);
+ }
+ @else if $position == right {
+ @include inner-side-shadow(left, $offcanvas-inner-shadow-size, $offcanvas-inner-shadow-color);
+ }
+ @else if $position == top {
+ @include inner-side-shadow(bottom, $offcanvas-inner-shadow-size, $offcanvas-inner-shadow-color);
+ }
+ @else if $position == bottom {
+ @include inner-side-shadow(top, $offcanvas-inner-shadow-size, $offcanvas-inner-shadow-color);
+ }
+ }
+ }
+
+}
+
+/// Sets the styles for the content container.
+@mixin off-canvas-content() {
+ transform: none;
+ backface-visibility: hidden;
+
+ // Bind to has-transition-X class to prevent transition for transform:none
+ &.has-transition-overlap,
+ &.has-transition-push {
+ transition: transform $offcanvas-transition-length $offcanvas-transition-timing;
+ }
+
+ // Transform scope until the element is closed (makes sure transitionend gets triggered)
+ &.has-transition-push {
+ transform: translate(0, 0);
+ }
+
+ // Consider element & content, nested in another content
+ .off-canvas.is-open {
+ transform: translate(0, 0);
+ }
+}
+
+/// Adds styles that reveal an off-canvas panel.
+@mixin off-canvas-reveal(
+$position: left,
+$zindex: $offcanvas-reveal-zindex,
+$content: $maincontent-class,
+$breakpoint: small
+) {
+ transform: none;
+ z-index: $zindex;
+ transition: none;
+ visibility: visible;
+
+ @if not $offcanvas-fixed-reveal {
+ position: absolute;
+ }
+
+ .close-button {
+ display: none;
+ }
+
+ // Consider revealed element is nested in content
+ .#{$maincontent-class} & {
+ transform: none;
+ }
+
+ @at-root .#{$content}.has-reveal-#{$position} {
+ margin-#{$position}: -zf-get-bp-val($offcanvas-sizes, $breakpoint);
+ }
+
+ // backwards compatibility (prior to v6.4)
+ & ~ .#{$content} {
+ margin-#{$position}: -zf-get-bp-val($offcanvas-sizes, $breakpoint);
+ }
+}
+
+/// Overrides the off-canvas styles
+@mixin in-canvas() {
+ visibility: visible;
+ height: auto;
+ position: static;
+ background: none;
+ width: auto;
+ overflow: visible;
+ transition: none;
+
+ // Increase CSS specificity
+ &.position-left,
+ &.position-right,
+ &.position-top,
+ &.position-bottom {
+ box-shadow: none;
+ transform: none;
+ }
+
+ .close-button {
+ display: none;
+ }
+}
+
+@mixin foundation-off-canvas {
+ @include off-canvas-basics;
+
+ // Off-canvas wrapper
+ .off-canvas-wrapper {
+ @include off-canvas-wrapper;
+ }
+
+ // Off-canvas container
+ .off-canvas {
+ @include off-canvas-base;
+
+ // Force position absolute for nested off-canvas because fixed doesn't work for push transition within the transform scope.
+ @at-root .#{$maincontent-class} & {
+ // NOTE: since overlap transition is currently forced if nested, there's no need to force position absolute until nested push transition is supported.
+ // position: absolute;
+ }
+ }
+
+ // Off-canvas container with absolute position
+ .off-canvas-absolute {
+ @include off-canvas-base($fixed: false);
+ }
+
+ // Off-canvas position classes
+ .position-left { @include off-canvas-position(left, horizontal); }
+ .position-right { @include off-canvas-position(right, horizontal); }
+ .position-top { @include off-canvas-position(top, vertical); }
+ .position-bottom { @include off-canvas-position(bottom, vertical); }
+
+ .off-canvas-content {
+ @include off-canvas-content;
+ }
+
+ // Reveal off-canvas panel on larger screens
+ @each $name, $value in $breakpoint-classes {
+ @if $name != $-zf-zero-breakpoint {
+ @include breakpoint($name) {
+ .position-left.reveal-for-#{$name} {
+ @include off-canvas-reveal(left, $offcanvas-reveal-zindex, $maincontent-class, $name);
+ }
+
+ .position-right.reveal-for-#{$name} {
+ @include off-canvas-reveal(right, $offcanvas-reveal-zindex, $maincontent-class, $name);
+ }
+
+ .position-top.reveal-for-#{$name} {
+ @include off-canvas-reveal(top, $offcanvas-reveal-zindex, $maincontent-class, $name);
+ }
+
+ .position-bottom.reveal-for-#{$name} {
+ @include off-canvas-reveal(bottom, $offcanvas-reveal-zindex, $maincontent-class, $name);
+ }
+ }
+ }
+ }
+
+ // Move in-canvas for larger screens
+ @each $name, $value in $breakpoint-classes {
+ @if $name != $-zf-zero-breakpoint {
+ @include breakpoint($name) {
+ .off-canvas.in-canvas-for-#{$name} {
+ @include in-canvas;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/foundation/components/_orbit.scss b/src/foundation/components/_orbit.scss
new file mode 100644
index 0000000..988c291
--- /dev/null
+++ b/src/foundation/components/_orbit.scss
@@ -0,0 +1,197 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group orbit
+////
+
+/// Default color for Orbit's bullets.
+/// @type Color
+$orbit-bullet-background: $medium-gray !default;
+
+/// Default active color for Orbit's bullets.
+/// @type Color
+$orbit-bullet-background-active: $dark-gray !default;
+
+/// Default diameter for Orbit's bullets.
+/// @type Number
+$orbit-bullet-diameter: 1.2rem !default;
+
+/// Default margin between Orbit's bullets.
+/// @type Number
+$orbit-bullet-margin: 0.1rem !default;
+
+/// Default distance from slide region for Orbit's bullets.
+/// @type Number
+$orbit-bullet-margin-top: 0.8rem !default;
+
+/// Default bottom margin from Orbit's bullets to whatever content may lurk below it.
+/// @type Number
+$orbit-bullet-margin-bottom: 0.8rem !default;
+
+/// Default background color for Orbit's caption.
+/// @type Color
+$orbit-caption-background: rgba($black, 0.5) !default;
+
+/// Default padding for Orbit's caption.
+/// @type Number
+$orbit-caption-padding: 1rem !default;
+
+/// Default background color for Orbit's controls when hovered.
+/// @type Color
+$orbit-control-background-hover: rgba($black, 0.5) !default;
+
+/// Default padding for Orbit's controls.
+/// @type Number
+$orbit-control-padding: 1rem !default;
+
+/// Default z-index for Orbit's controls.
+/// @type Number
+$orbit-control-zindex: 10 !default;
+
+/// Adds styles for the outer Orbit wrapper. These styles are used on the `.orbit` class.
+@mixin orbit-wrapper {
+ position: relative;
+}
+
+/// Adds styles for the inner Orbit slide container. These styles are used on the `.orbit-container` class.
+@mixin orbit-container {
+ position: relative;
+ height: 0; // Prevent FOUC by not showing until JS sets height
+ margin: 0;
+ list-style: none;
+ overflow: hidden;
+}
+
+/// Adds styles for the individual slides of an Orbit slider. These styles are used on the `.orbit-slide` class.
+@mixin orbit-slide {
+ width: 100%;
+ position: absolute;
+
+ &.no-motionui {
+ &.is-active {
+ top: 0;
+ left: 0;
+ }
+ }
+}
+
+@mixin orbit-figure {
+ margin: 0;
+}
+
+/// Adds styles for a slide containing an image. These styles are used on the `.orbit-image` class.
+@mixin orbit-image {
+ width: 100%;
+ max-width: 100%;
+ margin: 0;
+}
+
+/// Adds styles for an orbit slide caption. These styles are used on the `.orbit-caption` class.
+@mixin orbit-caption {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ margin-bottom: 0;
+ padding: $orbit-caption-padding;
+
+ background-color: $orbit-caption-background;
+ color: color-pick-contrast($orbit-caption-background);
+}
+
+/// Adds base styles for the next/previous buttons in an Orbit slider. These styles are shared between the `.orbit-next` and `.orbit-previous` classes in the default CSS.
+@mixin orbit-control {
+ @include disable-mouse-outline;
+ @include vertical-center;
+ z-index: $orbit-control-zindex;
+ padding: $orbit-control-padding;
+ color: $white;
+
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: $orbit-control-background-hover;
+ }
+}
+
+/// Adds styles for the Orbit previous button. These styles are used on the `.orbit-previous` class.
+@mixin orbit-previous {
+ #{$global-left}: 0;
+}
+
+/// Adds styles for the Orbit next button. These styles are used on the `.orbit-next` class.
+@mixin orbit-next {
+ #{$global-left}: auto;
+ #{$global-right}: 0;
+}
+
+/// Adds styles for a container of Orbit bullets. /// Adds styles for the Orbit previous button. These styles are used on the `.orbit-bullets` class.
+@mixin orbit-bullets {
+ @include disable-mouse-outline;
+ position: relative;
+ margin-top: $orbit-bullet-margin-top;
+ margin-bottom: $orbit-bullet-margin-bottom;
+ text-align: center;
+
+ button {
+ width: $orbit-bullet-diameter;
+ height: $orbit-bullet-diameter;
+ margin: $orbit-bullet-margin;
+
+ border-radius: 50%;
+ background-color: $orbit-bullet-background;
+
+ &:hover {
+ background-color: $orbit-bullet-background-active;
+ }
+
+ &.is-active {
+ background-color: $orbit-bullet-background-active;
+ }
+ }
+}
+
+@mixin foundation-orbit {
+ .orbit {
+ @include orbit-wrapper;
+ }
+
+ .orbit-container {
+ @include orbit-container;
+ }
+
+ .orbit-slide {
+ @include orbit-slide;
+ }
+
+ .orbit-figure {
+ @include orbit-figure;
+ }
+
+ .orbit-image {
+ @include orbit-image;
+ }
+
+ .orbit-caption {
+ @include orbit-caption;
+ }
+
+ %orbit-control {
+ @include orbit-control;
+ }
+
+ .orbit-previous {
+ @extend %orbit-control;
+ @include orbit-previous;
+ }
+
+ .orbit-next {
+ @extend %orbit-control;
+ @include orbit-next;
+ }
+
+ .orbit-bullets {
+ @include orbit-bullets;
+ }
+}
diff --git a/src/foundation/components/_pagination.scss b/src/foundation/components/_pagination.scss
new file mode 100644
index 0000000..b4095d4
--- /dev/null
+++ b/src/foundation/components/_pagination.scss
@@ -0,0 +1,201 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group pagination
+////
+
+/// Font size of pagination items.
+/// @type Number
+$pagination-font-size: rem-calc(14) !default;
+
+/// Default bottom margin of the pagination object.
+/// @type Number
+$pagination-margin-bottom: $global-margin !default;
+
+/// Text color of pagination items.
+/// @type Color
+$pagination-item-color: $black !default;
+
+/// Padding inside of pagination items.
+/// @type Number
+$pagination-item-padding: rem-calc(3 10) !default;
+
+/// Right margin to separate pagination items.
+/// @type Number
+$pagination-item-spacing: rem-calc(1) !default;
+
+/// Default radius for pagination items.
+/// @type Number
+$pagination-radius: $global-radius !default;
+
+/// Background color of pagination items on hover.
+/// @type Color
+$pagination-item-background-hover: $light-gray !default;
+
+/// Background color of pagination item for the current page.
+/// @type Color
+$pagination-item-background-current: $primary-color !default;
+
+/// Text color of the pagination item for the current page.
+/// @type Color
+$pagination-item-color-current: $white !default;
+
+/// Text color of a disabled pagination item.
+/// @type Color
+$pagination-item-color-disabled: $medium-gray !default;
+
+/// Color of the ellipsis in a pagination menu.
+/// @type Color
+$pagination-ellipsis-color: $black !default;
+
+/// If `false`, don't display page number links on mobile, only next/previous links
+/// and optionally current page number.
+/// @type Boolean
+$pagination-mobile-items: false !default;
+
+/// If `true`, display the current page number on mobile even if `$pagination-mobile-items` is set to `false`.
+/// This parameter will only override the visibility setting of the current item for `$pagination-mobile-items: false;`,
+/// it will not affect the current page number visibility when `$pagination-mobile-items` is set to `true`.
+/// @type Boolean
+$pagination-mobile-current-item: false !default;
+
+/// If `true`, arrows are added to the next and previous links of pagination.
+/// @type Boolean
+$pagination-arrows: true !default;
+
+/// Content for the previous arrow when `$pagination-arrows` is `true`
+/// @type String
+$pagination-arrow-previous: '\00AB' !default;
+
+/// Content for the next arrow when `$pagination-arrows` is `true`
+/// @type String
+$pagination-arrow-next: '\00BB' !default;
+
+/// Adds styles for a pagination container. Apply this to a `<ul>`.
+@mixin pagination-container (
+ $margin-bottom: $pagination-margin-bottom,
+ $font-size: $pagination-font-size,
+ $spacing: $pagination-item-spacing,
+ $radius: $pagination-radius,
+ $color: $pagination-item-color,
+ $padding: $pagination-item-padding,
+ $background-hover: $pagination-item-background-hover
+) {
+ @include clearfix;
+ margin-#{$global-left}: 0;
+ margin-bottom: $margin-bottom;
+
+ // List item
+ li {
+ margin-#{$global-right}: $spacing;
+ border-radius: $radius;
+ font-size: $font-size;
+
+ @if $pagination-mobile-items {
+ display: inline-block;
+ }
+ @else {
+ display: none;
+
+ &:last-child,
+ &:first-child {
+ display: inline-block;
+ }
+
+ @if $pagination-mobile-current-item {
+ &.current {
+ display: inline-block;
+ }
+ }
+
+ @include breakpoint(medium) {
+ display: inline-block;
+ }
+ }
+ }
+
+ // Page links
+ a,
+ button {
+ display: block;
+ padding: $padding;
+ border-radius: $radius;
+ color: $color;
+
+ &:hover {
+ background: $background-hover;
+ }
+ }
+}
+
+/// Adds styles for the current pagination item. Apply this to an `<a>`.
+@mixin pagination-item-current (
+ $padding: $pagination-item-padding,
+ $background-current: $pagination-item-background-current,
+ $color-current: $pagination-item-color-current
+) {
+ padding: $padding;
+ background: $background-current;
+ color: $color-current;
+ cursor: default;
+}
+
+/// Adds styles for a disabled pagination item. Apply this to an `<a>`.
+@mixin pagination-item-disabled (
+ $padding: $pagination-item-padding,
+ $color: $pagination-item-color-disabled
+) {
+ padding: $padding;
+ color: $color;
+ cursor: not-allowed;
+
+ &:hover {
+ background: transparent;
+ }
+}
+
+/// Adds styles for an ellipsis for use in a pagination list.
+@mixin pagination-ellipsis (
+ $padding: $pagination-item-padding,
+ $color: $pagination-ellipsis-color
+) {
+ padding: $padding;
+ content: '\2026';
+ color: $color;
+}
+
+@mixin foundation-pagination {
+ .pagination {
+ @include pagination-container;
+
+ .current {
+ @include pagination-item-current;
+ }
+
+ .disabled {
+ @include pagination-item-disabled;
+ }
+
+ .ellipsis::after {
+ @include pagination-ellipsis;
+ }
+ }
+
+ @if $pagination-arrows {
+ .pagination-previous a::before,
+ .pagination-previous.disabled::before {
+ display: inline-block;
+ margin-#{$global-right}: 0.5rem;
+ content: $pagination-arrow-previous;
+ }
+
+ .pagination-next a::after,
+ .pagination-next.disabled::after {
+ display: inline-block;
+ margin-#{$global-left}: 0.5rem;
+ content: $pagination-arrow-next;
+ }
+ }
+}
diff --git a/src/foundation/components/_progress-bar.scss b/src/foundation/components/_progress-bar.scss
new file mode 100644
index 0000000..7de5f87
--- /dev/null
+++ b/src/foundation/components/_progress-bar.scss
@@ -0,0 +1,63 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+/// Adds styles for a progress bar container.
+@mixin progress-container {
+ height: $progress-height;
+ margin-bottom: $progress-margin-bottom;
+ border-radius: $progress-radius;
+ background-color: $progress-background;
+}
+
+/// Adds styles for the inner meter of a progress bar.
+@mixin progress-meter {
+ position: relative;
+ display: block;
+ width: 0%;
+ height: 100%;
+ background-color: $progress-meter-background;
+
+ @if has-value($progress-radius) {
+ border-radius: $global-radius;
+ }
+}
+
+/// Adds styles for text in the progress meter.
+@mixin progress-meter-text {
+ @include absolute-center;
+ margin: 0;
+ font-size: 0.75rem;
+ font-weight: bold;
+ color: $white;
+ white-space: nowrap;
+
+ @if has-value($progress-radius) {
+ border-radius: $progress-radius;
+ }
+}
+
+@mixin foundation-progress-bar {
+ // Progress bar
+ .progress {
+ @include progress-container;
+
+ @each $name, $color in $foundation-palette {
+ &.#{$name} {
+ .progress-meter {
+ background-color: $color;
+ }
+ }
+ }
+ }
+
+ // Inner meter
+ .progress-meter {
+ @include progress-meter;
+ }
+
+ // Inner meter text
+ .progress-meter-text {
+ @include progress-meter-text;
+ }
+}
diff --git a/src/foundation/components/_responsive-embed.scss b/src/foundation/components/_responsive-embed.scss
new file mode 100644
index 0000000..8bcd899
--- /dev/null
+++ b/src/foundation/components/_responsive-embed.scss
@@ -0,0 +1,57 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group responsive-embed
+////
+
+/// Margin below a responsive embed container.
+/// @type Number
+$responsive-embed-margin-bottom: rem-calc(16) !default;
+
+/// Aspect ratios used to determine padding-bottom of responsive embed containers.
+/// @type Map
+$responsive-embed-ratios: (
+ default: 4 by 3,
+ widescreen: 16 by 9,
+) !default;
+
+/// Creates a responsive embed container.
+/// @param {String|List} $ratio [default] - Ratio of the container. Can be a key from the `$responsive-embed-ratios` map or a list formatted as `x by y`.
+@mixin responsive-embed($ratio: default) {
+ @if type-of($ratio) == 'string' {
+ $ratio: map-get($responsive-embed-ratios, $ratio);
+ }
+ position: relative;
+ height: 0;
+ margin-bottom: $responsive-embed-margin-bottom;
+ padding-bottom: ratio-to-percentage($ratio);
+ overflow: hidden;
+
+ iframe,
+ object,
+ embed,
+ video {
+ position: absolute;
+ top: 0;
+ #{$global-left}: 0;
+ width: 100%;
+ height: 100%;
+ }
+}
+
+@mixin foundation-responsive-embed {
+ .responsive-embed,
+ .flex-video {
+ @include responsive-embed($ratio: default);
+
+ $ratios: map-remove($responsive-embed-ratios,default);
+
+ @each $name, $ratio in $ratios {
+ &.#{$name} {
+ padding-bottom: ratio-to-percentage($ratio);
+ }
+ }
+ }
+}
diff --git a/src/foundation/components/_reveal.scss b/src/foundation/components/_reveal.scss
new file mode 100644
index 0000000..8099490
--- /dev/null
+++ b/src/foundation/components/_reveal.scss
@@ -0,0 +1,185 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group reveal
+////
+
+/// Default background color of a modal.
+/// @type Color
+$reveal-background: $white !default;
+
+/// Default width of a modal, with no class applied.
+/// @type Number
+$reveal-width: 600px !default;
+
+/// Default maximum width of a modal.
+/// @type Number
+$reveal-max-width: $global-width !default;
+
+/// Default padding inside a modal.
+/// @type Number
+$reveal-padding: $global-padding !default;
+
+/// Default border around a modal.
+/// @type Number
+$reveal-border: 1px solid $medium-gray !default;
+
+/// Default radius for modal.
+/// @type Number
+$reveal-radius: $global-radius !default;
+
+/// z-index for modals. The overlay uses this value, while the modal itself uses this value plus one.
+/// @type Number
+$reveal-zindex: 1005 !default;
+
+/// Background color of modal overlays.
+/// @type Color
+$reveal-overlay-background: rgba($black, 0.45) !default;
+
+
+// Placeholder selector for medium-and-up modals
+// Prevents duplicate CSS when defining multiple Reveal sizes
+// This should be in the same breakpoint then `@mixin reveal-modal-width`
+@include breakpoint(medium) {
+ %reveal-centered {
+ right: auto;
+ left: auto;
+ margin: 0 auto;
+ }
+}
+
+
+/// Adds styles for a modal overlay.
+/// @param {Color} $background [$reveal-overlay-background] - Background color of the overlay.
+@mixin reveal-overlay($background: $reveal-overlay-background) {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $reveal-zindex;
+
+ display: none;
+ background-color: $background;
+ overflow-y: auto;
+}
+
+/// Adds base styles for a modal.
+@mixin reveal-modal-base {
+ @include disable-mouse-outline;
+ z-index: $reveal-zindex + 1;
+ // Workaround android browser z-index bug
+ backface-visibility: hidden;
+
+ display: none;
+ padding: $reveal-padding;
+
+ border: $reveal-border;
+ border-radius: $reveal-radius;
+ background-color: $reveal-background;
+
+ @include breakpoint(medium) {
+ min-height: 0;
+ }
+
+ // Make sure rows don't have a min-width on them
+ .column {
+ min-width: 0;
+ }
+
+ // Strip margins from the last item in the modal
+ > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+/// Adjusts the width of a modal.
+/// @param {Number} $width - Width of the modal. Generally a percentage.
+/// @param {Number} $max-width [$reveal-max-width] - Maximum width of the modal.
+@mixin reveal-modal-width(
+ $width: $reveal-width,
+ $max-width: $reveal-max-width
+) {
+ // Extends must be made outside of breakpoints for compatibility with newer Sass versions (libsass v3.5)
+ @extend %reveal-centered;
+ @include breakpoint(medium) {
+ width: $width;
+ max-width: $max-width;
+ }
+}
+
+/// Creates a full-screen modal, which stretches the full width and height of the window.
+@mixin reveal-modal-fullscreen {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+
+ width: 100%;
+ max-width: none;
+ height: 100%;
+ min-height: 100%;
+ margin-left: 0;
+
+ border: 0;
+ border-radius: 0;
+}
+
+@mixin foundation-reveal {
+
+ /// Disables the scroll when Reveal is shown to prevent the background from shifting
+ html.is-reveal-open {
+ position: fixed;
+ width: 100%;
+ overflow-y: hidden;
+
+ &.zf-has-scroll {
+ overflow-y: scroll;
+ }
+
+ body { // sass-lint:disable-line no-qualifying-elements
+ overflow-y: hidden;
+ }
+ }
+
+ // Overlay
+ .reveal-overlay {
+ @include reveal-overlay;
+ }
+
+ // Modal container
+ .reveal {
+ @include reveal-modal-base;
+ @include reveal-modal-width($reveal-width);
+ position: relative;
+ top: 100px;
+ margin-right: auto;
+ margin-left: auto;
+ overflow-y: auto;
+
+ // Remove padding
+ &.collapse {
+ padding: 0;
+ }
+
+ // Sizing classes
+ &.tiny { @include reveal-modal-width(30%); }
+ &.small { @include reveal-modal-width(50%); }
+ &.large { @include reveal-modal-width(90%); }
+
+ // Full-screen mode
+ &.full {
+ @include reveal-modal-fullscreen;
+ }
+
+ @include breakpoint($-zf-zero-breakpoint only) {
+ @include reveal-modal-fullscreen;
+ }
+
+ &.without-overlay {
+ position: fixed;
+ }
+ }
+}
diff --git a/src/foundation/components/_slider.scss b/src/foundation/components/_slider.scss
new file mode 100755
index 0000000..c32b228
--- /dev/null
+++ b/src/foundation/components/_slider.scss
@@ -0,0 +1,137 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+// [TODO] Check how plugin confirms disabled or vertical status
+// [TODO] Check if transition: all; is necessary
+
+////
+/// @group slider
+////
+
+/// Default slider width of a vertical slider. (Doesn't apply to the native slider.)
+/// @type Number
+$slider-width-vertical: 0.5rem !default;
+
+/// Transition properties to apply to the slider handle and fill. (Doesn't apply to the native slider.)
+/// @type Transition
+$slider-transition: all 0.2s ease-in-out !default;
+
+/// Adds the general styles for sliders.
+@mixin slider-container {
+ position: relative;
+ height: $slider-height;
+ margin-top: 1.25rem;
+ margin-bottom: 2.25rem;
+
+ background-color: $slider-background;
+ cursor: pointer;
+ user-select: none;
+ touch-action: none;
+}
+
+/// Adds the general styles for active fill for sliders.
+@mixin slider-fill {
+ position: absolute;
+ top: 0;
+ left: 0;
+
+ display: inline-block;
+ max-width: 100%;
+ height: $slider-height;
+
+ background-color: $slider-fill-background;
+ transition: $slider-transition;
+
+ &.is-dragging {
+ transition: all 0s linear;
+ }
+}
+
+/// Adds the general styles for the slider handles.
+@mixin slider-handle {
+ @include disable-mouse-outline;
+ @include vertical-center;
+ left: 0;
+ z-index: 1;
+
+ display: inline-block;
+ width: $slider-handle-width;
+ height: $slider-handle-height;
+
+ border-radius: $slider-radius;
+ background-color: $slider-handle-background;
+ transition: $slider-transition;
+ touch-action: manipulation;
+
+ &:hover {
+ background-color: scale-color($slider-handle-background, $lightness: -15%);
+ }
+
+ &.is-dragging {
+ transition: all 0s linear;
+ }
+}
+
+@mixin slider-disabled {
+ opacity: $slider-opacity-disabled;
+ cursor: not-allowed;
+}
+
+@mixin slider-vertical {
+ display: inline-block;
+ width: $slider-width-vertical;
+ height: 12.5rem;
+ margin: 0 1.25rem;
+ transform: scale(1, -1);
+
+ .slider-fill {
+ top: 0;
+ width: $slider-width-vertical;
+ max-height: 100%;
+ }
+
+ .slider-handle {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ width: $slider-handle-height;
+ height: $slider-handle-width;
+ transform: translateX(-50%);
+ }
+}
+
+@mixin foundation-slider {
+ // Container
+ .slider {
+ @include slider-container;
+ }
+
+ // Fill area
+ .slider-fill {
+ @include slider-fill;
+ }
+
+ // Draggable handle
+ .slider-handle {
+ @include slider-handle;
+ }
+
+ // Disabled state
+ .slider.disabled,
+ .slider[disabled] {
+ @include slider-disabled;
+ }
+
+ // Vertical slider
+ .slider.vertical {
+ @include slider-vertical;
+ }
+
+ // RTL support
+ @if $global-text-direction == rtl {
+ .slider:not(.vertical) {
+ transform: scale(-1, 1);
+ }
+ }
+}
diff --git a/src/foundation/components/_sticky.scss b/src/foundation/components/_sticky.scss
new file mode 100644
index 0000000..378e0e2
--- /dev/null
+++ b/src/foundation/components/_sticky.scss
@@ -0,0 +1,39 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+@mixin foundation-sticky {
+ .sticky-container {
+ position: relative;
+ }
+
+ .sticky {
+ position: relative;
+ z-index: 0;
+ transform: translate3d(0, 0, 0);
+ }
+
+ .sticky.is-stuck {
+ position: fixed;
+ z-index: 5;
+ width: 100%;
+
+ &.is-at-top {
+ top: 0;
+ }
+
+ &.is-at-bottom {
+ bottom: 0;
+ }
+ }
+
+ .sticky.is-anchored {
+ position: relative;
+ right: auto;
+ left: auto;
+
+ &.is-at-bottom {
+ bottom: 0;
+ }
+ }
+}
diff --git a/src/foundation/components/_switch.scss b/src/foundation/components/_switch.scss
new file mode 100644
index 0000000..b1056da
--- /dev/null
+++ b/src/foundation/components/_switch.scss
@@ -0,0 +1,261 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group switch
+////
+
+/// Background color of a switch.
+/// @type Color
+$switch-background: $medium-gray !default;
+
+/// Background active color of a switch.
+/// @type Color
+$switch-background-active: $primary-color !default;
+
+/// Height of a switch, with no class applied.
+/// @type Number
+$switch-height: 2rem !default;
+
+/// Height of a switch with .tiny class.
+/// @type Number
+$switch-height-tiny: 1.5rem !default;
+
+/// Height of a switch with .small class.
+/// @type Number
+$switch-height-small: 1.75rem !default;
+
+/// Height of a switch with .large class.
+/// @type Number
+$switch-height-large: 2.5rem !default;
+
+/// Border radius of the switch
+/// @type Number
+$switch-radius: $global-radius !default;
+
+/// border around a modal.
+/// @type Number
+$switch-margin: $global-margin !default;
+
+/// Background color for the switch container and paddle.
+/// @type Color
+$switch-paddle-background: $white !default;
+
+/// Spacing between a switch paddle and the edge of the body.
+/// @type Number
+$switch-paddle-offset: 0.25rem !default;
+
+/// border radius of the switch paddle
+/// @type Number
+$switch-paddle-radius: $global-radius !default;
+
+/// switch transition.
+/// @type Number
+$switch-paddle-transition: all 0.25s ease-out !default;
+
+/// Opacity of a disabled switch.
+/// @type Number
+$switch-opacity-disabled: .5 !default;
+
+/// Cursor for a disabled switch.
+/// @type Cursor
+$switch-cursor-disabled: not-allowed !default;
+
+// make them variables
+// ask about accessibility on label
+// change class name for text
+
+/// Adds styles for a switch container. Apply this to a container class.
+@mixin switch-container {
+ position: relative;
+ margin-bottom: $switch-margin;
+ outline: 0;
+
+ // These properties cascade down to the switch text
+ font-size: rem-calc(14);
+ font-weight: bold;
+ color: $white;
+
+ user-select: none;
+}
+
+/// Adds styles for a switch input. Apply this to an `<input>` within a switch.
+@mixin switch-input {
+ position: absolute;
+ margin-bottom: 0;
+ opacity: 0;
+}
+
+/// Adds styles for the background and paddle of a switch. Apply this to a `<label>` within a switch.
+@mixin switch-paddle {
+ $switch-width: $switch-height * 2;
+ $paddle-height: $switch-height - ($switch-paddle-offset * 2);
+ $paddle-width: $switch-height - ($switch-paddle-offset * 2);
+ $paddle-active-offest: $switch-width - $paddle-width - $switch-paddle-offset;
+
+ position: relative;
+ display: block;
+ width: $switch-width;
+ height: $switch-height;
+
+ border-radius: $switch-radius;
+ background: $switch-background;
+ transition: $switch-paddle-transition;
+
+ // Resetting these <label> presets so type styles cascade down
+ font-weight: inherit;
+ color: inherit;
+
+ cursor: pointer;
+
+ // Needed to override specificity
+ input + & {
+ margin: 0;
+ }
+
+ // The paddle itself
+ &::after {
+ position: absolute;
+ top: $switch-paddle-offset;
+ #{$global-left}: $switch-paddle-offset;
+
+ display: block;
+ width: $paddle-width;
+ height: $paddle-height;
+
+ transform: translate3d(0, 0, 0);
+ border-radius: $switch-paddle-radius;
+ background: $switch-paddle-background;
+ transition: $switch-paddle-transition;
+ content: '';
+ }
+
+ // Change the visual style when the switch is active
+ input:checked ~ & {
+ background: $switch-background-active;
+
+ &::after {
+ #{$global-left}: $paddle-active-offest;
+ }
+ }
+
+ // indicate a disabled switch
+ input:disabled ~ & {
+ cursor: $switch-cursor-disabled;
+ opacity: $switch-opacity-disabled;
+ }
+
+ input:focus ~ & {
+ @include disable-mouse-outline;
+ }
+}
+
+/// Adds base styles for active/inactive text inside a switch. Apply this to text elements inside the switch `<label>`.
+@mixin switch-text {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+/// Adds styles for the active state text within a switch.
+@mixin switch-text-active {
+ #{$global-left}: 8%;
+ display: none;
+
+ input:checked + label > & {
+ display: block;
+ }
+}
+
+/// Adds styles for the inactive state text within a switch.
+@mixin switch-text-inactive {
+ #{$global-right}: 15%;
+
+ input:checked + label > & {
+ display: none;
+ }
+}
+
+/// Changes the size of a switch by modifying the size of the body and paddle. Apply this to a switch container.
+/// @param {Number} $font-size [1rem] - Font size of label text within the switch.
+/// @param {Number} $switch-height [2rem] - Height of the switch body.
+/// @param {Number} $paddle-offset [0.25rem] - Spacing between the switch paddle and the edge of the switch body.
+@mixin switch-size(
+ $font-size: 1rem,
+ $switch-height: 2rem,
+ $paddle-offset: 0.25rem
+) {
+
+ $switch-width: $switch-height * 2;
+ $paddle-width: $switch-height - ($paddle-offset * 2);
+ $paddle-height: $switch-height - ($paddle-offset * 2);
+ $paddle-active-offest: $switch-width - $paddle-width - $paddle-offset;
+
+ height: $switch-height;
+
+ .switch-paddle {
+ width: $switch-width;
+ height: $switch-height;
+ font-size: $font-size;
+ }
+
+ .switch-paddle::after {
+ top: $paddle-offset;
+ #{$global-left}: $paddle-offset;
+ width: $paddle-width;
+ height: $paddle-height;
+ }
+
+ input:checked ~ .switch-paddle::after {
+ #{$global-left}: $paddle-active-offest;
+ }
+}
+
+@mixin foundation-switch {
+ // Container class
+ .switch {
+ height: $switch-height;
+ @include switch-container;
+ }
+
+ // <input> element
+ .switch-input {
+ @include switch-input;
+ }
+
+ // <label> element
+ .switch-paddle {
+ @include switch-paddle;
+ }
+
+ // Base label text styles
+ %switch-text {
+ @include switch-text;
+ }
+
+ // Active label text styles
+ .switch-active {
+ @extend %switch-text;
+ @include switch-text-active;
+ }
+
+ // Inactive label text styles
+ .switch-inactive {
+ @extend %switch-text;
+ @include switch-text-inactive;
+ }
+
+ // Switch sizes
+ .switch.tiny {
+ @include switch-size(rem-calc(10), $switch-height-tiny, $switch-paddle-offset);
+ }
+
+ .switch.small {
+ @include switch-size(rem-calc(12), $switch-height-small, $switch-paddle-offset);
+ }
+
+ .switch.large {
+ @include switch-size(rem-calc(16), $switch-height-large, $switch-paddle-offset);
+ }
+}
diff --git a/src/foundation/components/_table.scss b/src/foundation/components/_table.scss
new file mode 100644
index 0000000..4c44aa3
--- /dev/null
+++ b/src/foundation/components/_table.scss
@@ -0,0 +1,328 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+// sass-lint:disable no-qualifying-elements
+
+////
+/// @group table
+////
+
+/// Default color for table background.
+/// @type Color
+$table-background: $white !default;
+
+/// Default scale for darkening the striped table rows and the table border.
+/// @type Number
+$table-color-scale: 5% !default;
+
+/// Default style for table border.
+/// @type List
+$table-border: 1px solid smart-scale($table-background, $table-color-scale) !default;
+
+/// Default padding for table.
+/// @type Number
+$table-padding: rem-calc(8 10 10) !default;
+
+/// Default scale for darkening the table rows on hover.
+/// @type Number
+$table-hover-scale: 2% !default;
+
+/// Default color of standard rows on hover.
+/// @type List
+$table-row-hover: darken($table-background, $table-hover-scale) !default;
+
+/// Default color of striped rows on hover.
+/// @type List
+$table-row-stripe-hover: darken($table-background, $table-color-scale + $table-hover-scale) !default;
+
+/// If `true`, tables are striped by default and an .unstriped class is created. If `false`, a .striped class is created.
+/// @type Boolean
+$table-is-striped: true !default;
+
+/// Default background color for striped rows.
+/// @type Color
+$table-striped-background: smart-scale($table-background, $table-color-scale) !default;
+
+/// Default value for showing the stripe on rows of the tables, excluding the header and footer. If even, the even rows will have a background color. If odd, the odd rows will have a background color. If empty, or any other value, the table rows will have no striping.
+/// @type Keyword
+$table-stripe: even !default;
+
+/// Default color for header background.
+/// @type Color
+$table-head-background: smart-scale($table-background, $table-color-scale / 2) !default;
+
+/// Default color of header rows on hover.
+/// @type List
+$table-head-row-hover: darken($table-head-background, $table-hover-scale) !default;
+
+/// Default color for footer background.
+/// @type Color
+$table-foot-background: smart-scale($table-background, $table-color-scale) !default;
+
+/// Default color of footer rows on hover.
+/// @type List
+$table-foot-row-hover: darken($table-foot-background, $table-hover-scale) !default;
+
+/// Default font color for header.
+/// @type Color
+$table-head-font-color: $body-font-color !default;
+
+/// Default font color for footer.
+/// @type Color
+$table-foot-font-color: $body-font-color !default;
+
+/// Default value for showing the header when using stacked tables.
+/// @type Boolean
+$show-header-for-stacked: false !default;
+
+/// Breakpoint at which stacked table switches from mobile to desktop view.
+/// @type Breakpoint
+$table-stack-breakpoint: medium !default;
+
+@mixin -zf-table-stripe($stripe: $table-stripe) {
+ tr {
+ // If stripe is set to even, darken the even rows.
+ @if $stripe == even {
+ &:nth-child(even) {
+ border-bottom: 0;
+ background-color: $table-striped-background;
+ }
+ }
+
+ // If stripe is set to odd, darken the odd rows.
+ @else if $stripe == odd {
+ &:nth-child(odd) {
+ background-color: $table-striped-background;
+ }
+ }
+ }
+}
+
+@mixin -zf-table-unstripe() {
+ tr {
+ border-bottom: 0;
+ border-bottom: $table-border;
+ background-color: $table-background;
+ }
+}
+
+@mixin -zf-table-children-styles($stripe: $table-stripe, $is-striped: $table-is-striped) {
+ thead,
+ tbody,
+ tfoot {
+ border: $table-border;
+ background-color: $table-background;
+ }
+
+ // Caption
+ caption {
+ padding: $table-padding;
+ font-weight: $global-weight-bold;
+ }
+
+ // Table head
+ thead {
+ background: $table-head-background;
+ color: $table-head-font-color;
+ }
+
+ // Table foot
+ tfoot {
+ background: $table-foot-background;
+ color: $table-foot-font-color;
+ }
+
+ // Table head and foot
+ thead,
+ tfoot {
+ // Rows within head and foot
+ tr {
+ background: transparent;
+ }
+
+ // Cells within head and foot
+ th,
+ td {
+ padding: $table-padding;
+ font-weight: $global-weight-bold;
+ text-align: #{$global-left};
+ }
+ }
+
+ // Table rows
+ tbody {
+ th,
+ td {
+ padding: $table-padding;
+ }
+ }
+
+ // If tables are striped
+ @if $is-striped == true {
+ tbody {
+ @include -zf-table-stripe($stripe);
+ }
+
+ &.unstriped {
+ tbody {
+ @include -zf-table-unstripe();
+ background-color: $table-background;
+ }
+ }
+ }
+
+ // If tables are not striped
+ @else if $is-striped == false {
+ tbody {
+ @include -zf-table-unstripe();
+ }
+
+ &.striped {
+ tbody {
+ @include -zf-table-stripe($stripe);
+ }
+ }
+ }
+}
+
+/// Adds the general styles for tables.
+/// @param {Keyword} $stripe [$table-stripe] - Uses keywords even, odd, or none to darken rows of the table. The default value is even.
+/// @param {Boolean} $nest [false] - Needed if you only want to apply this to a specific table.
+@mixin table(
+ $stripe: $table-stripe,
+ $nest: false
+) {
+ border-collapse: collapse;
+ width: 100%;
+ margin-bottom: $global-margin;
+ border-radius: $global-radius;
+
+ @if $nest {
+ @include -zf-table-children-styles($stripe);
+ }
+ @else {
+ @at-root {
+ @include -zf-table-children-styles($stripe);
+ }
+ }
+}
+
+/// Adds the ability to horizontally scroll the table when the content overflows horizontally.
+@mixin table-scroll {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+}
+
+/// Slightly darkens the table rows on hover.
+@mixin table-hover {
+ thead tr {
+ //Darkens the table header rows on hover.
+ &:hover {
+ background-color: $table-head-row-hover;
+ }
+ }
+
+ tfoot tr {
+ //Darkens the table footer rows on hover.
+ &:hover {
+ background-color: $table-foot-row-hover;
+ }
+ }
+
+ tbody tr {
+ //Darkens the non-striped table rows on hover.
+ &:hover {
+ background-color: $table-row-hover;
+ }
+ }
+
+ @if $table-is-striped == true {
+ // Darkens the even striped table rows.
+ @if($table-stripe == even) {
+ &:not(.unstriped) tr:nth-of-type(even):hover {
+ background-color: $table-row-stripe-hover;
+ }
+ }
+
+ // Darkens the odd striped table rows.
+ @else if($table-stripe == odd) {
+ &:not(.unstriped) tr:nth-of-type(odd):hover {
+ background-color: $table-row-stripe-hover;
+ }
+ }
+ }
+
+ @else if $table-is-striped == false {
+ // Darkens the even striped table rows.
+ @if($table-stripe == even) {
+ &.striped tr:nth-of-type(even):hover {
+ background-color: $table-row-stripe-hover;
+ }
+ }
+
+ // Darkens the odd striped table rows.
+ @else if($table-stripe == odd) {
+ &.striped tr:nth-of-type(odd):hover {
+ background-color: $table-row-stripe-hover;
+ }
+ }
+ }
+}
+
+/// Adds styles for a stacked table. Useful for small-screen layouts.
+/// @param {Boolean} $header [$show-header-for-stacked] - Show the first th of header when stacked.
+@mixin table-stack($header: $show-header-for-stacked) {
+ @if $header {
+ thead {
+ th {
+ display: block;
+ }
+ }
+ }
+ @else {
+ thead {
+ display: none;
+ }
+ }
+
+ tfoot {
+ display: none;
+ }
+
+ tr,
+ th,
+ td {
+ display: block;
+ }
+
+ td {
+ border-top: 0;
+ }
+}
+
+@mixin foundation-table($nest: false) {
+ table {
+ @include table($nest: $nest);
+ }
+
+ table.stack {
+ @include breakpoint($table-stack-breakpoint down) {
+ @include table-stack;
+ }
+ }
+
+ table.scroll {
+ @include table-scroll;
+ }
+
+ table.hover {
+ @include table-hover;
+ }
+
+ .table-scroll {
+ overflow-x: auto;
+
+ }
+}
diff --git a/src/foundation/components/_tabs.scss b/src/foundation/components/_tabs.scss
new file mode 100644
index 0000000..ecf85ed
--- /dev/null
+++ b/src/foundation/components/_tabs.scss
@@ -0,0 +1,193 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group tabs
+////
+
+/// Default margin of the tab bar.
+/// @type Number
+$tab-margin: 0 !default;
+
+/// Default background color of a tab bar.
+/// @type Color
+$tab-background: $white !default;
+
+/// Font color of tab item.
+/// @type Color
+$tab-color: $primary-color !default;
+
+/// Active background color of a tab bar.
+/// @type Color
+$tab-background-active: $light-gray !default;
+
+/// Active font color of tab item.
+/// @type Color
+$tab-active-color: $primary-color !default;
+
+/// Font size of tab items.
+/// @type Number
+$tab-item-font-size: rem-calc(12) !default;
+
+/// Default background color on hover for items in a Menu.
+$tab-item-background-hover: $white !default;
+
+/// Default padding of a tab item.
+/// @type Number
+$tab-item-padding: 1.25rem 1.5rem !default;
+
+/// Default background color of tab content.
+/// @type Color
+$tab-content-background: $white !default;
+
+/// Default border color of tab content.
+/// @type Color
+$tab-content-border: $light-gray !default;
+
+/// Default text color of tab content.
+/// @type Color
+$tab-content-color: $body-font-color !default;
+
+/// Default padding for tab content.
+/// @type Number | List
+$tab-content-padding: 1rem !default;
+
+/// Adds styles for a tab container. Apply this to a `<ul>`.
+@mixin tabs-container (
+ $margin: $tab-margin,
+ $background: $tab-background,
+ $border-color: $tab-content-border
+) {
+ @include clearfix;
+ margin: $margin;
+ border: 1px solid $border-color;
+ background: $background;
+ list-style-type: none;
+}
+
+/// Augments a tab container to have vertical tabs. Use this in conjunction with `tabs-container()`.
+@mixin tabs-container-vertical {
+ > li {
+ display: block;
+ float: none;
+ width: auto;
+ }
+}
+
+/// Adds styles for the links within a tab container. Apply this to the `<li>` elements inside a tab container.
+@mixin tabs-title (
+ $padding: $tab-item-padding,
+ $font-size: $tab-item-font-size,
+ $color: $tab-color,
+ $color-active: $tab-active-color,
+ $background-hover: $tab-item-background-hover,
+ $background-active: $tab-background-active
+) {
+ float: #{$global-left};
+
+ > a {
+ @include disable-mouse-outline;
+ display: block;
+ padding: $padding;
+ font-size: $font-size;
+ line-height: 1;
+ color: $color;
+
+ &:hover {
+ background: $background-hover;
+ color: scale-color($color, $lightness: -14%);
+ }
+
+ &:focus,
+ &[aria-selected='true'] {
+ background: $background-active;
+ color: $color-active;
+ }
+ }
+}
+
+/// Adds styles for the wrapper that surrounds a tab group's content panes.
+@mixin tabs-content (
+ $background: $tab-content-background,
+ $color: $tab-content-color,
+ $border-color: $tab-content-border
+) {
+ border: 1px solid $border-color;
+ border-top: 0;
+ background: $background;
+ color: $color;
+ transition: all 0.5s ease;
+}
+
+/// Augments a tab content container to have a vertical style, by shifting the border around. Use this in conjunction with `tabs-content()`.
+@mixin tabs-content-vertical (
+ $border-color: $tab-content-border
+) {
+ border: 1px solid $border-color;
+ border-#{$global-left}: 0;
+}
+
+/// Adds styles for an individual tab content panel within the tab content container.
+@mixin tabs-panel (
+ $padding: $tab-content-padding
+) {
+ display: none;
+ padding: $padding;
+
+ &.is-active {
+ display: block;
+ }
+}
+
+@mixin foundation-tabs {
+ .tabs {
+ @include tabs-container;
+ }
+
+ // Vertical
+ .tabs.vertical {
+ @include tabs-container-vertical;
+ }
+
+ // Simple
+ .tabs.simple {
+ > li > a {
+ padding: 0;
+
+ &:hover {
+ background: transparent;
+ }
+ }
+ }
+
+ // Primary color
+ .tabs.primary {
+ background: $primary-color;
+
+ > li > a {
+ color: color-pick-contrast($primary-color);
+
+ &:hover,
+ &:focus {
+ background: smart-scale($primary-color);
+ }
+ }
+ }
+
+ .tabs-title {
+ @include tabs-title;
+ }
+
+ .tabs-content {
+ @include tabs-content;
+ }
+
+ .tabs-content.vertical {
+ @include tabs-content-vertical;
+ }
+
+ .tabs-panel {
+ @include tabs-panel;
+ }
+}
diff --git a/src/foundation/components/_thumbnail.scss b/src/foundation/components/_thumbnail.scss
new file mode 100644
index 0000000..ed8109b
--- /dev/null
+++ b/src/foundation/components/_thumbnail.scss
@@ -0,0 +1,67 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group thumbnail
+////
+
+/// Border around thumbnail images.
+/// @type Border
+$thumbnail-border: 4px solid $white !default;
+
+/// Bottom margin for thumbnail images.
+/// @type Length
+$thumbnail-margin-bottom: $global-margin !default;
+
+/// Box shadow under thumbnail images.
+/// @type Shadow
+$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2) !default;
+
+/// Box shadow under thumbnail images.
+/// @type Shadow
+$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5) !default;
+
+/// Transition proprties for thumbnail images.
+/// @type Transition
+$thumbnail-transition: box-shadow 200ms ease-out !default;
+
+/// Default radius for thumbnail images.
+/// @type Number
+$thumbnail-radius: $global-radius !default;
+
+/// Adds thumbnail styles to an element.
+@mixin thumbnail {
+ display: inline-block;
+ max-width: 100%;
+ margin-bottom: $thumbnail-margin-bottom;
+
+ border: $thumbnail-border;
+ border-radius: $thumbnail-radius;
+ box-shadow: $thumbnail-shadow;
+
+ line-height: 0;
+}
+
+@mixin thumbnail-link {
+ transition: $thumbnail-transition;
+
+ &:hover,
+ &:focus {
+ box-shadow: $thumbnail-shadow-hover;
+ }
+
+ image {
+ box-shadow: none;
+ }
+}
+
+@mixin foundation-thumbnail {
+ .thumbnail {
+ @include thumbnail;
+ }
+
+ a.thumbnail {
+ @include thumbnail-link;
+ }
+}
diff --git a/src/foundation/components/_title-bar.scss b/src/foundation/components/_title-bar.scss
new file mode 100644
index 0000000..e0f370c
--- /dev/null
+++ b/src/foundation/components/_title-bar.scss
@@ -0,0 +1,84 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group title-bar
+////
+
+/// Background color of a title bar.
+/// @type Color
+$titlebar-background: $black !default;
+
+/// Color of text inside a title bar.
+/// @type Color
+$titlebar-color: $white !default;
+
+/// Padding inside a title bar.
+/// @type Length
+$titlebar-padding: 0.5rem !default;
+
+/// Font weight of text inside a title bar.
+/// @type Weight
+$titlebar-text-font-weight: bold !default;
+
+/// Color of menu icons inside a title bar.
+/// @type Color
+$titlebar-icon-color: $white !default;
+
+/// Color of menu icons inside a title bar on hover.
+/// @type Color
+$titlebar-icon-color-hover: $medium-gray !default;
+
+/// Spacing between the menu icon and text inside a title bar.
+/// @type Length
+$titlebar-icon-spacing: 0.25rem !default;
+
+@mixin foundation-title-bar {
+ .title-bar {
+ padding: $titlebar-padding;
+ background: $titlebar-background;
+ color: $titlebar-color;
+
+ @if $global-flexbox {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ }
+ @else {
+ @include clearfix;
+ }
+
+ .menu-icon {
+ margin-#{$global-left}: $titlebar-icon-spacing;
+ margin-#{$global-right}: $titlebar-icon-spacing;
+ }
+ }
+
+ @if $global-flexbox {
+ .title-bar-left,
+ .title-bar-right {
+ flex: 1 1 0px; // sass-lint:disable-line zero-unit
+ }
+
+ .title-bar-right {
+ text-align: right;
+ }
+ }
+ @else {
+ .title-bar-left {
+ float: left;
+ }
+
+ .title-bar-right {
+ float: right;
+ text-align: right;
+ }
+ }
+
+ .title-bar-title {
+ display: inline-block;
+ vertical-align: middle;
+ font-weight: $titlebar-text-font-weight;
+ }
+}
diff --git a/src/foundation/components/_tooltip.scss b/src/foundation/components/_tooltip.scss
new file mode 100644
index 0000000..36dc155
--- /dev/null
+++ b/src/foundation/components/_tooltip.scss
@@ -0,0 +1,160 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group tooltip
+////
+
+/// Default cursor of the defined term.
+/// @type Keyword
+$has-tip-cursor: help !default;
+
+/// Default font weight of the defined term.
+/// @type Keyword | Number
+$has-tip-font-weight: $global-weight-bold !default;
+
+/// Default border bottom of the defined term.
+/// @type List
+$has-tip-border-bottom: dotted 1px $dark-gray !default;
+
+/// Default color of the tooltip background.
+/// @type Color
+$tooltip-background-color: $black !default;
+
+/// Default color of the tooltip font.
+/// @type Color
+$tooltip-color: $white !default;
+
+/// Default padding of the tooltip background.
+/// @type Number
+$tooltip-padding: 0.75rem !default;
+
+/// Default max width for tooltips.
+/// @type Number
+$tooltip-max-width: 10rem !default;
+
+/// Default font size of the tooltip text. By default, we recommend a smaller font size than the body copy.
+/// @type Number
+$tooltip-font-size: $small-font-size !default;
+
+/// Default pip width for tooltips.
+/// @type Number
+$tooltip-pip-width: 0.75rem !default;
+
+/// Default pip height for tooltips. This is helpful for calculating the distance of the tooltip from the tooltip word.
+/// @type Number
+$tooltip-pip-height: $tooltip-pip-width * 0.866 !default;
+
+/// Default radius for tooltips.
+/// @type Number
+$tooltip-radius: $global-radius !default;
+
+@mixin has-tip {
+ position: relative;
+ display: inline-block;
+
+ border-bottom: $has-tip-border-bottom;
+ font-weight: $has-tip-font-weight;
+ cursor: $has-tip-cursor;
+}
+
+@mixin tooltip {
+ position: absolute;
+ top: calc(100% + #{$tooltip-pip-height});
+ z-index: 1200;
+
+ max-width: $tooltip-max-width;
+ padding: $tooltip-padding;
+
+ border-radius: $tooltip-radius;
+ background-color: $tooltip-background-color;
+ font-size: $tooltip-font-size;
+ color: $tooltip-color;
+
+ &::before {
+ position: absolute;
+ }
+
+ &.bottom {
+ &::before {
+ @include css-triangle($tooltip-pip-width, $tooltip-background-color, up);
+ bottom: 100%;
+ }
+
+ &.align-center::before {
+ left: 50%;
+ transform: translateX(-50%);
+ }
+ }
+
+ &.top {
+ &::before {
+ @include css-triangle($tooltip-pip-width, $tooltip-background-color, down);
+ top: 100%;
+ bottom: auto;
+ }
+
+ &.align-center::before {
+ left: 50%;
+ transform: translateX(-50%);
+ }
+ }
+
+ &.left {
+ &::before {
+ @include css-triangle($tooltip-pip-width, $tooltip-background-color, right);
+ left: 100%;
+ }
+
+ &.align-center::before {
+ bottom: auto;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+ }
+
+ &.right {
+ &::before {
+ @include css-triangle($tooltip-pip-width, $tooltip-background-color, left);
+ right: 100%;
+ left: auto;
+ }
+
+ &.align-center::before {
+ bottom: auto;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+ }
+
+ &.align-top::before {
+ bottom: auto;
+ top: 10%;
+ }
+
+ &.align-bottom::before {
+ bottom: 10%;
+ top: auto;
+ }
+
+ &.align-left::before {
+ left: 10%;
+ right: auto;
+ }
+
+ &.align-right::before {
+ left: auto;
+ right: 10%;
+ }
+}
+
+@mixin foundation-tooltip {
+ .has-tip {
+ @include has-tip;
+ }
+
+ .tooltip {
+ @include tooltip;
+ }
+}
diff --git a/src/foundation/components/_top-bar.scss b/src/foundation/components/_top-bar.scss
new file mode 100644
index 0000000..2503a7f
--- /dev/null
+++ b/src/foundation/components/_top-bar.scss
@@ -0,0 +1,175 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group top-bar
+////
+
+/// Padding for the top bar.
+/// @type Number
+$topbar-padding: 0.5rem !default;
+
+/// Background color for the top bar. This color also cascades to menus within the top bar.
+/// @type Color
+$topbar-background: $light-gray !default;
+
+/// Background color submenus within the top bar. Usefull if $topbar-background is transparent.
+/// @type Color
+$topbar-submenu-background: $topbar-background !default;
+
+/// Spacing for the top bar title.
+/// @type Number
+$topbar-title-spacing: 0.5rem 1rem 0.5rem 0 !default;
+
+/// Maximum width of `<input>` elements inside the top bar.
+/// @type Number
+$topbar-input-width: 200px !default;
+
+/// Breakpoint at which top bar switches from mobile to desktop view.
+/// @type Breakpoint
+$topbar-unstack-breakpoint: medium !default;
+
+/// Adds styles for a top bar container.
+@mixin top-bar-container {
+ @if $global-flexbox {
+ display: flex;
+ flex-wrap: nowrap;
+ justify-content: space-between;
+ align-items: center;
+ }
+ @else {
+ @include clearfix;
+ }
+
+ padding: $topbar-padding;
+
+ &,
+ ul {
+ background-color: $topbar-background;
+ }
+
+ // Check if $topbar-background is differnt from $topbar-background-submenu
+ @if ($topbar-background != $topbar-submenu-background) {
+ ul ul {
+ background-color: $topbar-submenu-background;
+ }
+ }
+
+ // Restrain width of inputs by default to make them easier to arrange
+ input {
+ max-width: $topbar-input-width;
+ margin-#{$global-right}: 1rem;
+ }
+
+ // The above styles shouldn't apply to input group fields
+ .input-group-field {
+ width: 100%;
+ margin-#{$global-right}: 0;
+ }
+
+ input.button { // sass-lint:disable-line no-qualifying-elements
+ width: auto;
+ }
+}
+
+/// Makes sections of a top bar stack on top of each other.
+@mixin top-bar-stacked {
+ @if $global-flexbox {
+ flex-wrap: wrap;
+
+ // Sub-sections
+ .top-bar-left,
+ .top-bar-right {
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ }
+ @else {
+ // Sub-sections
+ .top-bar-left,
+ .top-bar-right {
+ width: 100%;
+ }
+ }
+}
+
+/// Undoes the CSS applied by the `top-bar-stacked()` mixin.
+@mixin top-bar-unstack {
+ @if $global-flexbox {
+ flex-wrap: nowrap;
+
+ .top-bar-left {
+ flex: 1 1 auto;
+ margin-right: auto;
+ }
+
+ .top-bar-right {
+ flex: 0 1 auto;
+ margin-left: auto;
+ }
+ }
+ @else {
+ .top-bar-left,
+ .top-bar-right {
+ width: auto;
+ }
+ }
+}
+
+@mixin foundation-top-bar {
+ // Top bar container
+ .top-bar {
+ @include top-bar-container;
+
+ // Stack on small screens by default
+ @include top-bar-stacked;
+
+ @include breakpoint($topbar-unstack-breakpoint) {
+ @include top-bar-unstack;
+ }
+
+ // Generate classes for stacking on each screen size (defined in $breakpoint-classes)
+ @each $size in $breakpoint-classes {
+ @if $size != $-zf-zero-breakpoint {
+ &.stacked-for-#{$size} {
+ @include breakpoint($size down) {
+ @include top-bar-stacked;
+ }
+ }
+ }
+ }
+ }
+
+ // Sub-sections
+ @if $global-flexbox {
+ .top-bar-title {
+ flex: 0 0 auto;
+ margin: $topbar-title-spacing;
+ }
+
+ .top-bar-left,
+ .top-bar-right {
+ flex: 0 0 auto;
+ }
+ }
+ @else {
+ .top-bar-title {
+ display: inline-block;
+ float: left;
+ padding: $topbar-title-spacing;
+
+ .menu-icon {
+ bottom: 2px;
+ }
+ }
+
+ .top-bar-left {
+ float: left;
+ }
+
+ .top-bar-right {
+ float: right;
+ }
+ }
+}
diff --git a/src/foundation/components/_visibility.scss b/src/foundation/components/_visibility.scss
new file mode 100644
index 0000000..30bf7cd
--- /dev/null
+++ b/src/foundation/components/_visibility.scss
@@ -0,0 +1,135 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+/// Hide an element by default, only displaying it above a certain screen size.
+/// @param {Keyword} $size - Breakpoint to use. **Must be a breakpoint defined in `$breakpoints`.**
+@mixin show-for($size) {
+ $size: map-get($breakpoints, $size);
+ // Max value is 0.2px under the next breakpoint (0.02 / 16 = 0.00125).
+ // Use a precision under 1px to support browser zoom, but not to low to avoid rounding.
+ // See https://github.com/zurb/foundation-sites/issues/11313
+ $size: -zf-bp-to-em($size) - .00125;
+
+ @include breakpoint($size down) {
+ display: none !important;
+ }
+}
+
+/// Hide an element by default, only displaying it within a certain breakpoint.
+/// @param {Keyword} $size - Breakpoint to use. **Must be a breakpoint defined in `$breakpoints`.**
+@mixin show-for-only($size) {
+ $lower-bound-size: map-get($breakpoints, $size);
+ $upper-bound-size: -zf-map-next($breakpoints, $size);
+
+ // more often than not this will be correct, just one time round the loop it won't so set in scope here
+ $lower-bound: -zf-bp-to-em($lower-bound-size) - .00125;
+ // test actual lower-bound-size, if 0 set it to 0em
+ @if strip-unit($lower-bound-size) == 0 {
+ $lower-bound: -zf-bp-to-em($lower-bound-size);
+ }
+
+ @if $upper-bound-size == null {
+ @media screen and (max-width: $lower-bound) {
+ display: none !important;
+ }
+ }
+ @else {
+ $upper-bound: -zf-bp-to-em($upper-bound-size);
+
+ @media screen and (max-width: $lower-bound), screen and (min-width: $upper-bound) {
+ display: none !important;
+ }
+ }
+}
+
+
+/// Show an element by default, and hide it above a certain screen size.
+/// @param {Keyword} $size - Breakpoint to use. **Must be a breakpoint defined in `$breakpoints`.**
+@mixin hide-for($size) {
+ @include breakpoint($size) {
+ display: none !important;
+ }
+}
+
+/// Show an element by default, and hide it above a certain screen size.
+/// @param {Keyword} $size - Breakpoint to use. **Must be a breakpoint defined in `$breakpoints`.**
+@mixin hide-for-only($size) {
+ @include breakpoint($size only) {
+ display: none !important;
+ }
+}
+
+@mixin foundation-visibility-classes {
+ // Basic hiding classes
+ .hide {
+ display: none !important;
+ }
+
+ .invisible {
+ visibility: hidden;
+ }
+
+ // Responsive visibility classes
+ @each $size in $breakpoint-classes {
+ @if $size != $-zf-zero-breakpoint {
+ .hide-for-#{$size} {
+ @include hide-for($size);
+ }
+
+ .show-for-#{$size} {
+ @include show-for($size);
+ }
+ }
+
+ .hide-for-#{$size}-only {
+ @include hide-for-only($size);
+ }
+
+ .show-for-#{$size}-only {
+ @include show-for-only($size);
+ }
+ }
+
+ // Screen reader visibility classes
+ // Need a "hide-for-sr" class? Add aria-hidden='true' to the element
+ .show-for-sr,
+ .show-on-focus {
+ @include element-invisible;
+ }
+
+ // Only display the element when it's focused
+ .show-on-focus {
+ &:active,
+ &:focus {
+ @include element-invisible-off;
+ }
+ }
+
+ // Landscape and portrait visibility
+ .show-for-landscape,
+ .hide-for-portrait {
+ display: block !important;
+
+ @include breakpoint(landscape) {
+ display: block !important;
+ }
+
+ @include breakpoint(portrait) {
+ display: none !important;
+ }
+ }
+
+ .hide-for-landscape,
+ .show-for-portrait {
+ display: none !important;
+
+ @include breakpoint(landscape) {
+ display: none !important;
+ }
+
+ @include breakpoint(portrait) {
+ display: block !important;
+ }
+ }
+}
diff --git a/src/foundation/forms/_checkbox.scss b/src/foundation/forms/_checkbox.scss
new file mode 100644
index 0000000..60e8bfc
--- /dev/null
+++ b/src/foundation/forms/_checkbox.scss
@@ -0,0 +1,41 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group forms
+////
+
+@mixin foundation-form-checkbox {
+ [type='file'],
+ [type='checkbox'],
+ [type='radio'] {
+ margin: 0 0 $form-spacing;
+ }
+
+ // Styles for input/label siblings
+ [type='checkbox'] + label,
+ [type='radio'] + label {
+ display: inline-block;
+ vertical-align: baseline;
+
+ margin-#{$global-left}: $form-spacing * 0.5;
+ margin-#{$global-right}: $form-spacing;
+ margin-bottom: 0;
+
+ &[for] {
+ cursor: pointer;
+ }
+ }
+
+ // Styles for inputs inside labels
+ label > [type='checkbox'],
+ label > [type='radio'] {
+ margin-#{$global-right}: $form-spacing * 0.5;
+ }
+
+ // Normalize file input width
+ [type='file'] {
+ width: 100%;
+ }
+}
diff --git a/src/foundation/forms/_error.scss b/src/foundation/forms/_error.scss
new file mode 100644
index 0000000..eb2fdc1
--- /dev/null
+++ b/src/foundation/forms/_error.scss
@@ -0,0 +1,89 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group abide
+////
+
+/// Sets if error styles should be added to inputs.
+/// @type Boolean
+$abide-inputs: true !default;
+
+/// Sets if error styles should be added to labels.
+/// @type Boolean
+$abide-labels: true !default;
+
+/// Background color to use for invalid text inputs.
+/// @type Color
+$input-background-invalid: get-color(alert) !default;
+
+/// Color to use for labels of invalid inputs.
+/// @type Color
+$form-label-color-invalid: get-color(alert) !default;
+
+/// Default font color for form error text.
+/// @type Color
+$input-error-color: get-color(alert) !default;
+
+/// Default font size for form error text.
+/// @type Number
+$input-error-font-size: rem-calc(12) !default;
+
+/// Default font weight for form error text.
+/// @type Keyword
+$input-error-font-weight: $global-weight-bold !default;
+
+/// Styles the background and border of an input field to have an error state.
+///
+/// @param {Color} $background [$alert-color] - Color to use for the background and border.
+/// @param {Number} $background-lighten [10%] - Lightness level of the background color.
+@mixin form-input-error(
+ $background: $input-background-invalid,
+ $background-lighten: 10%
+) {
+ &:not(:focus) {
+ border-color: $background;
+ background-color: mix($background, $white, $background-lighten);
+
+ &::placeholder {
+ color: $background;
+ }
+ }
+}
+
+/// Adds error styles to a form element, using the values in the settings file.
+@mixin form-error {
+ display: none;
+ margin-top: $form-spacing * -0.5;
+ margin-bottom: $form-spacing;
+
+ font-size: $input-error-font-size;
+ font-weight: $input-error-font-weight;
+ color: $input-error-color;
+}
+
+@mixin foundation-form-error {
+ @if $abide-inputs {
+ // Error class for invalid inputs
+ .is-invalid-input {
+ @include form-input-error;
+ }
+ }
+
+ @if $abide-labels {
+ // Error class for labels of invalid outputs
+ .is-invalid-label {
+ color: $form-label-color-invalid;
+ }
+ }
+
+ // Form error element
+ .form-error {
+ @include form-error;
+
+ &.is-visible {
+ display: block;
+ }
+ }
+}
diff --git a/src/foundation/forms/_fieldset.scss b/src/foundation/forms/_fieldset.scss
new file mode 100644
index 0000000..8611d5a
--- /dev/null
+++ b/src/foundation/forms/_fieldset.scss
@@ -0,0 +1,53 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group forms
+////
+
+/// Default border around custom fieldsets.
+/// @type Border
+$fieldset-border: 1px solid $medium-gray !default;
+
+/// Default padding inside custom fieldsets.
+/// @type Number
+$fieldset-padding: rem-calc(20) !default;
+
+/// Default margin around custom fieldsets.
+/// @type Number
+$fieldset-margin: rem-calc(18 0) !default;
+
+/// Default padding between the legend text and fieldset border.
+/// @type Number
+$legend-padding: rem-calc(0 3) !default;
+
+@mixin fieldset {
+ margin: $fieldset-margin;
+ padding: $fieldset-padding;
+ border: $fieldset-border;
+
+ legend {
+ // Covers up the fieldset's border to create artificial padding
+ margin: 0;
+ margin-#{$global-left}: rem-calc(-3);
+ padding: $legend-padding;
+ }
+}
+
+@mixin foundation-form-fieldset {
+ fieldset {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ }
+
+ legend {
+ max-width: 100%;
+ margin-bottom: $form-spacing * 0.5;
+ }
+
+ .fieldset {
+ @include fieldset;
+ }
+}
diff --git a/src/foundation/forms/_forms.scss b/src/foundation/forms/_forms.scss
new file mode 100644
index 0000000..1507fda
--- /dev/null
+++ b/src/foundation/forms/_forms.scss
@@ -0,0 +1,34 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group forms
+////
+
+/// Global spacing for form elements.
+/// @type Number
+$form-spacing: rem-calc(16) !default;
+
+@import 'text';
+@import 'checkbox';
+@import 'label';
+@import 'help-text';
+@import 'input-group';
+@import 'fieldset';
+@import 'select';
+@import 'range';
+@import 'progress';
+@import 'meter';
+@import 'error';
+
+@mixin foundation-forms {
+ @include foundation-form-text;
+ @include foundation-form-checkbox;
+ @include foundation-form-label;
+ @include foundation-form-helptext;
+ @include foundation-form-prepostfix;
+ @include foundation-form-fieldset;
+ @include foundation-form-select;
+ @include foundation-form-error;
+}
diff --git a/src/foundation/forms/_help-text.scss b/src/foundation/forms/_help-text.scss
new file mode 100644
index 0000000..04e5237
--- /dev/null
+++ b/src/foundation/forms/_help-text.scss
@@ -0,0 +1,30 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group forms
+////
+
+/// Default color for help text.
+/// @type Color
+$helptext-color: $black !default;
+
+/// Default font size for help text.
+/// @type Number
+$helptext-font-size: rem-calc(13) !default;
+
+/// Default font style for help text.
+/// @type Keyword
+$helptext-font-style: italic !default;
+
+@mixin foundation-form-helptext {
+ .help-text {
+ $margin-top: ($form-spacing * 0.5) * -1;
+
+ margin-top: $margin-top;
+ font-size: $helptext-font-size;
+ font-style: $helptext-font-style;
+ color: $helptext-color;
+ }
+}
diff --git a/src/foundation/forms/_input-group.scss b/src/foundation/forms/_input-group.scss
new file mode 100644
index 0000000..46669e9
--- /dev/null
+++ b/src/foundation/forms/_input-group.scss
@@ -0,0 +1,142 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group forms
+////
+
+/// Color of labels prefixed to an input.
+/// @type Color
+$input-prefix-color: $black !default;
+
+/// Background color of labels prefixed to an input.
+/// @type Color
+$input-prefix-background: $light-gray !default;
+
+/// Border around labels prefixed to an input.
+/// @type Border
+$input-prefix-border: 1px solid $medium-gray !default;
+
+/// Left/right padding of an pre/postfixed input label
+$input-prefix-padding: 1rem !default;
+
+@mixin foundation-form-prepostfix {
+ $height: ($input-font-size * $input-line-height) + (get-side($input-padding, 'top') + get-side($input-padding, 'bottom')) - rem-calc(1);
+
+ .input-group {
+ display: if($global-flexbox, flex, table);
+ width: 100%;
+ margin-bottom: $form-spacing;
+
+ @if $global-flexbox {
+ align-items: stretch;
+ }
+
+ > :first-child {
+ &, &.input-group-button > * {
+ border-radius: if($global-text-direction == rtl, 0 $input-radius $input-radius 0, $input-radius 0 0 $input-radius);
+ }
+ }
+
+ > :last-child {
+ &, &.input-group-button > * {
+ border-radius: if($global-text-direction == rtl, $input-radius 0 0 $input-radius, 0 $input-radius $input-radius 0);
+ }
+ }
+ }
+
+ %input-group-child {
+ margin: 0;
+ white-space: nowrap;
+
+ @if not $global-flexbox {
+ display: table-cell;
+ vertical-align: middle;
+ }
+ }
+
+ .input-group-label {
+ @extend %input-group-child;
+ padding: 0 $input-prefix-padding;
+ border: $input-prefix-border;
+ background: $input-prefix-background;
+
+ color: $input-prefix-color;
+ text-align: center;
+ white-space: nowrap;
+
+ @if $global-flexbox {
+ display: flex;
+ flex: 0 0 auto;
+ align-items: center;
+ }
+ @else {
+ width: 1%;
+ height: 100%;
+ }
+
+ @if has-value($input-prefix-border) {
+ &:first-child {
+ border-#{$global-right}: 0;
+ }
+
+ &:last-child {
+ border-#{$global-left}: 0;
+ }
+ }
+ }
+
+ .input-group-field {
+ @extend %input-group-child;
+ border-radius: 0;
+
+ @if $global-flexbox {
+ flex: 1 1 0px; // sass-lint:disable-line zero-unit
+ min-width: 0;
+ }
+ }
+
+ .input-group-button {
+ @extend %input-group-child;
+ padding-top: 0;
+ padding-bottom: 0;
+ text-align: center;
+
+ @if $global-flexbox {
+ display: flex;
+ flex: 0 0 auto;
+ }
+ @else {
+ width: 1%;
+ height: 100%;
+ }
+
+ a,
+ input,
+ button,
+ label {
+ @extend %input-group-child;
+
+ @if $global-flexbox {
+ align-self: stretch;
+ height: auto;
+ }
+ @else {
+ height: $height;
+ }
+ padding-top: 0;
+ padding-bottom: 0;
+ font-size: $input-font-size;
+ }
+ }
+
+ // Specificity bump needed to prevent override by buttons
+ @if not $global-flexbox {
+ .input-group {
+ .input-group-button {
+ display: table-cell;
+ }
+ }
+ }
+}
diff --git a/src/foundation/forms/_label.scss b/src/foundation/forms/_label.scss
new file mode 100644
index 0000000..1c38851
--- /dev/null
+++ b/src/foundation/forms/_label.scss
@@ -0,0 +1,50 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group forms
+////
+
+/// Color for form labels.
+/// @type Color
+$form-label-color: $black !default;
+
+/// Font size for form labels.
+/// @type Number
+$form-label-font-size: rem-calc(14) !default;
+
+/// Font weight for form labels.
+/// @type Keyword
+$form-label-font-weight: $global-weight-normal !default;
+
+/// Line height for form labels. The higher the number, the more space between the label and its input field.
+/// @type Number
+$form-label-line-height: 1.8 !default;
+
+@mixin form-label {
+ display: block;
+ margin: 0;
+
+ font-size: $form-label-font-size;
+ font-weight: $form-label-font-weight;
+ line-height: $form-label-line-height;
+ color: $form-label-color;
+}
+
+@mixin form-label-middle {
+ $input-border-width: get-border-value($input-border, width);
+
+ margin: 0 0 $form-spacing;
+ padding: ($form-spacing / 2 + rem-calc($input-border-width)) 0;
+}
+
+@mixin foundation-form-label {
+ label {
+ @include form-label;
+
+ &.middle {
+ @include form-label-middle;
+ }
+ }
+}
diff --git a/src/foundation/forms/_meter.scss b/src/foundation/forms/_meter.scss
new file mode 100644
index 0000000..adc7457
--- /dev/null
+++ b/src/foundation/forms/_meter.scss
@@ -0,0 +1,116 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group meter
+////
+
+/// Height of a `<meter>` element.
+/// @type Length
+$meter-height: 1rem !default;
+
+/// Border radius of a `<meter>` element.
+/// @type Length
+$meter-radius: $global-radius !default;
+
+/// Background color of a `<meter>` element.
+/// @type Color
+$meter-background: $medium-gray !default;
+
+/// Meter fill for an optimal value in a `<meter>` element.
+/// @type Color
+$meter-fill-good: $success-color !default;
+
+/// Meter fill for an average value in a `<meter>` element.
+/// @type Color
+$meter-fill-medium: $warning-color !default;
+
+/// Meter fill for a suboptimal value in a `<meter>` element.
+/// @type Color
+$meter-fill-bad: $alert-color !default;
+
+@mixin foundation-meter-element {
+ meter {
+ display: block;
+ width: 100%;
+ height: $meter-height;
+ margin-bottom: 1rem;
+
+ // Disable `-webkit-appearance: none` from getting prefixed,
+ // We have disabled autoprefixer first and are just only using
+ // `-moz-appearance: none` as a prefix and neglecting the webkit.
+
+ /*! autoprefixer: off */
+ -moz-appearance: none; // sass-lint:disable-line no-vendor-prefixes
+ appearance: none;
+
+ @if has-value($meter-radius) {
+ border-radius: $meter-radius;
+ }
+
+ // For Firefox
+ border: 0;
+ background: $meter-background;
+
+ // Chrome/Safari/Edge
+ &::-webkit-meter-bar {
+ border: 0;
+ @if has-value($meter-radius) {
+ border-radius: $meter-radius;
+ }
+
+ background: $meter-background;
+ }
+
+ &::-webkit-meter-inner-element {
+ @if has-value($meter-radius) {
+ border-radius: $meter-radius;
+ }
+ }
+
+ &::-webkit-meter-optimum-value {
+ background: $meter-fill-good;
+
+ @if has-value($meter-radius) {
+ border-radius: $meter-radius;
+ }
+ }
+
+ &::-webkit-meter-suboptimum-value {
+ background: $meter-fill-medium;
+
+ @if has-value($meter-radius) {
+ border-radius: $meter-radius;
+ }
+ }
+
+ &::-webkit-meter-even-less-good-value {
+ background: $meter-fill-bad;
+
+ @if has-value($meter-radius) {
+ border-radius: $meter-radius;
+ }
+ }
+
+ &::-moz-meter-bar {
+ background: $primary-color;
+
+ @if has-value($meter-radius) {
+ border-radius: $meter-radius;
+ }
+ }
+
+ &:-moz-meter-optimum::-moz-meter-bar {
+ background: $meter-fill-good;
+ }
+
+ &:-moz-meter-sub-optimum::-moz-meter-bar {
+ background: $meter-fill-medium;
+ }
+
+ &:-moz-meter-sub-sub-optimum::-moz-meter-bar {
+ background: $meter-fill-bad;
+ }
+ }
+}
diff --git a/src/foundation/forms/_progress.scss b/src/foundation/forms/_progress.scss
new file mode 100644
index 0000000..9365470
--- /dev/null
+++ b/src/foundation/forms/_progress.scss
@@ -0,0 +1,94 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group progress-bar
+////
+
+/// Height of a progress bar.
+/// @type Number
+$progress-height: 1rem !default;
+
+/// Background color of a progress bar.
+/// @type Color
+$progress-background: $medium-gray !default;
+
+/// Bottom margin of a progress bar.
+/// @type Number
+$progress-margin-bottom: $global-margin !default;
+
+/// Default color of a progress bar's meter.
+/// @type Color
+$progress-meter-background: $primary-color !default;
+
+/// Default radius of a progress bar.
+/// @type Number
+$progress-radius: $global-radius !default;
+
+@mixin foundation-progress-element {
+ progress {
+ display: block;
+ width: 100%;
+ height: $progress-height;
+ margin-bottom: $progress-margin-bottom;
+
+ appearance: none;
+
+ @if has-value($progress-radius) {
+ border-radius: $progress-radius;
+ }
+
+ // For Firefox
+ border: 0;
+ background: $progress-background;
+
+ &::-webkit-progress-bar {
+ background: $progress-background;
+
+ @if has-value($progress-radius) {
+ border-radius: $progress-radius;
+ }
+ }
+
+ &::-webkit-progress-value {
+ background: $progress-meter-background;
+
+ @if has-value($progress-radius) {
+ border-radius: $progress-radius;
+ }
+ }
+
+ &::-moz-progress-bar {
+ background: $progress-meter-background;
+
+ @if has-value($progress-radius) {
+ border-radius: $progress-radius;
+ }
+ }
+
+ @each $name, $color in $foundation-palette {
+ &.#{$name} {
+ // Internet Explorer sets the fill with color
+ color: $color;
+
+ &::-webkit-progress-value {
+ background: $color;
+ }
+
+ &::-moz-progress-bar {
+ background: $color;
+ }
+ }
+ }
+
+ // For IE and Edge
+ &::-ms-fill { // sass-lint:disable-line no-vendor-prefixes
+ @if has-value($progress-radius) {
+ border-radius: $progress-radius;
+ }
+
+ border: 0;
+ }
+ }
+}
diff --git a/src/foundation/forms/_range.scss b/src/foundation/forms/_range.scss
new file mode 100644
index 0000000..06599f6
--- /dev/null
+++ b/src/foundation/forms/_range.scss
@@ -0,0 +1,149 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group slider
+////
+
+/// Default height of the slider.
+/// @type Number
+$slider-height: 0.5rem !default;
+
+/// Default background color of the slider's track.
+/// @type Color
+$slider-background: $light-gray !default;
+
+/// Default color of the active fill color of the slider.
+/// @type Color
+$slider-fill-background: $medium-gray !default;
+
+/// Default height of the handle of the slider.
+/// @type Number
+$slider-handle-height: 1.4rem !default;
+
+/// Default width of the handle of the slider.
+/// @type Number
+$slider-handle-width: 1.4rem !default;
+
+/// Default color of the handle for the slider.
+/// @type Color
+$slider-handle-background: $primary-color !default;
+
+/// Default fade amount of a disabled slider.
+/// @type Number
+$slider-opacity-disabled: 0.25 !default;
+
+/// Default radius for slider.
+/// @type Number
+$slider-radius: $global-radius !default;
+
+@mixin foundation-range-input {
+ input[type='range'] { // sass-lint:disable-line no-qualifying-elements
+ $margin: ($slider-handle-height - $slider-height) / 2;
+
+ display: block;
+ width: 100%;
+ height: auto;
+ margin-top: $margin;
+ margin-bottom: $margin;
+
+ appearance: none;
+ border: 0;
+ line-height: 1;
+ cursor: pointer;
+
+ @if has-value($slider-radius) {
+ border-radius: $slider-radius;
+ }
+
+ &:focus {
+ outline: 0;
+ }
+
+ &[disabled] {
+ opacity: $slider-opacity-disabled;
+ }
+
+ // sass-lint:disable no-vendor-prefix
+
+ // Chrome/Safari
+ &::-webkit-slider-runnable-track {
+ height: $slider-height;
+ background: $slider-background;
+ }
+
+ &::-webkit-slider-thumb {
+ width: $slider-handle-width;
+ height: $slider-handle-height;
+ margin-top: -$margin;
+
+ -webkit-appearance: none; // sass-lint:disable-line no-vendor-prefixes
+ background: $slider-handle-background;
+
+ @if has-value($slider-radius) {
+ border-radius: $slider-radius;
+ }
+ }
+
+ // Firefox
+ &::-moz-range-track {
+ height: $slider-height;
+ -moz-appearance: none; // sass-lint:disable-line no-vendor-prefixes
+ background: $slider-background;
+ }
+
+ &::-moz-range-thumb {
+ width: $slider-handle-width;
+ height: $slider-handle-height;
+ margin-top: -$margin;
+
+ -moz-appearance: none; // sass-lint:disable-line no-vendor-prefixes
+ background: $slider-handle-background;
+
+ @if has-value($slider-radius) {
+ border-radius: $slider-radius;
+ }
+ }
+
+ // Internet Explorer
+ &::-ms-track {
+ height: $slider-height;
+
+ border: 0;
+ border-top: $margin solid $body-background;
+ border-bottom: $margin solid $body-background;
+ background: $slider-background;
+
+ overflow: visible;
+ color: transparent;
+ }
+
+ &::-ms-thumb {
+ width: $slider-handle-width;
+ height: $slider-handle-height;
+ border: 0;
+ background: $slider-handle-background;
+
+ @if has-value($slider-radius) {
+ border-radius: $slider-radius;
+ }
+ }
+
+ &::-ms-fill-lower {
+ background: $slider-fill-background;
+ }
+
+ &::-ms-fill-upper {
+ background: $slider-background;
+ }
+
+ @at-root {
+ output {
+ vertical-align: middle;
+ margin-left: 0.5em;
+ line-height: $slider-handle-height;
+ }
+ }
+ }
+}
diff --git a/src/foundation/forms/_select.scss b/src/foundation/forms/_select.scss
new file mode 100644
index 0000000..3cda925
--- /dev/null
+++ b/src/foundation/forms/_select.scss
@@ -0,0 +1,90 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group forms
+////
+
+/// Background color for select menus.
+/// @type Color
+$select-background: $white !default;
+
+/// Color of the dropdown triangle inside select menus. Set to `transparent` to remove it entirely.
+/// @type Color
+$select-triangle-color: $dark-gray !default;
+
+/// Default radius for select menus.
+/// @type Color
+$select-radius: $global-radius !default;
+
+@mixin form-select {
+ $height: ($input-font-size * unitless-calc($input-line-height)) + (get-side($input-padding, 'top') + get-side($input-padding, 'bottom')) - rem-calc(1);
+
+ height: $height;
+ margin: 0 0 $form-spacing;
+ padding: $input-padding;
+
+ appearance: none;
+ border: $input-border;
+ border-radius: $select-radius;
+ background-color: $select-background;
+
+ font-family: $input-font-family;
+ font-size: $input-font-size;
+ font-weight: $input-font-weight;
+ line-height: $input-line-height;
+ color: $input-color;
+
+ @if $select-triangle-color != transparent {
+ @include background-triangle($select-triangle-color);
+ background-origin: content-box;
+ background-position: $global-right (-$form-spacing) center;
+ background-repeat: no-repeat;
+ background-size: 9px 6px;
+
+ padding-#{$global-right}: ($form-spacing * 1.5);
+ }
+
+ @if has-value($input-transition) {
+ transition: $input-transition;
+ }
+
+ // Focus state
+ &:focus {
+ outline: none;
+ border: $input-border-focus;
+ background-color: $input-background-focus;
+ box-shadow: $input-shadow-focus;
+
+ @if has-value($input-transition) {
+ transition: $input-transition;
+ }
+ }
+
+ // Disabled state
+ &:disabled {
+ background-color: $input-background-disabled;
+ cursor: $input-cursor-disabled;
+ }
+
+ // Hide the dropdown arrow shown in newer IE versions
+ &::-ms-expand {
+ display: none;
+ }
+
+ &[multiple] {
+ height: auto;
+ background-image: none;
+ }
+ &:not([multiple]) {
+ padding-top: 0;
+ padding-bottom: 0;
+ }
+}
+
+@mixin foundation-form-select {
+ select {
+ @include form-select;
+ }
+}
diff --git a/src/foundation/forms/_text.scss b/src/foundation/forms/_text.scss
new file mode 100644
index 0000000..778f035
--- /dev/null
+++ b/src/foundation/forms/_text.scss
@@ -0,0 +1,179 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group forms
+////
+
+/// Font color of text inputs.
+/// @type Color
+$input-color: $black !default;
+
+/// Font color of placeholder text within text inputs.
+/// @type Color
+$input-placeholder-color: $medium-gray !default;
+
+/// Font family of text inputs.
+/// @type Font
+$input-font-family: inherit !default;
+
+/// Font size of text inputs.
+/// @type Number
+$input-font-size: rem-calc(16) !default;
+
+/// Font weight of text inputs.
+/// @type Keyword
+$input-font-weight: $global-weight-normal !default;
+
+/// Line height of text inputs.
+/// @type Keyword
+$input-line-height: $global-lineheight !default;
+
+/// Background color of text inputs.
+/// @type Color
+$input-background: $white !default;
+
+/// Background color of focused of text inputs.
+/// @type Color
+$input-background-focus: $white !default;
+
+/// Background color of disabled text inputs.
+/// @type Color
+$input-background-disabled: $light-gray !default;
+
+/// Border around text inputs.
+/// @type Border
+$input-border: 1px solid $medium-gray !default;
+
+/// Border around focused text inputs.
+/// @type Color
+$input-border-focus: 1px solid $dark-gray !default;
+
+/// Padding of text inputs.
+/// @type Color
+$input-padding: $form-spacing / 2 !default;
+
+/// Box shadow inside text inputs when not focused.
+/// @type Shadow
+$input-shadow: inset 0 1px 2px rgba($black, 0.1) !default;
+
+/// Box shadow outside text inputs when focused.
+/// @type Shadow
+$input-shadow-focus: 0 0 5px $medium-gray !default;
+
+/// Cursor to use when hovering over a disabled text input.
+/// @type Cursor
+$input-cursor-disabled: not-allowed !default;
+
+/// Properties to transition on text inputs.
+/// @type Transition
+$input-transition: box-shadow 0.5s, border-color 0.25s ease-in-out !default;
+
+/// Enables the up/down buttons that Chrome and Firefox add to `<input type='number'>` elements.
+/// @type Boolean
+$input-number-spinners: true !default;
+
+/// Radius for text inputs.
+/// @type Border
+$input-radius: $global-radius !default;
+
+/// Border radius for form buttons, defaulted to global-radius.
+/// @type Number
+$form-button-radius: $global-radius !default;
+
+@mixin form-element {
+ $height: ($input-font-size * unitless-calc($input-line-height)) + (get-side($input-padding, 'top') + get-side($input-padding, 'bottom')) - rem-calc(1);
+
+ display: block;
+ box-sizing: border-box;
+ width: 100%;
+ height: $height;
+ margin: 0 0 $form-spacing;
+ padding: $input-padding;
+
+ border: $input-border;
+ border-radius: $input-radius;
+ background-color: $input-background;
+ box-shadow: $input-shadow;
+
+ font-family: $input-font-family;
+ font-size: $input-font-size;
+ font-weight: $input-font-weight;
+ line-height: $input-line-height;
+ color: $input-color;
+
+ @if has-value($input-transition) {
+ transition: $input-transition;
+ }
+
+ // Focus state
+ &:focus {
+ outline: none;
+ border: $input-border-focus;
+ background-color: $input-background-focus;
+ box-shadow: $input-shadow-focus;
+
+ @if has-value($input-transition) {
+ transition: $input-transition;
+ }
+ }
+}
+
+@mixin foundation-form-text {
+ // Text inputs
+ #{text-inputs()},
+ textarea {
+ @include form-element;
+ appearance: none;
+ }
+
+ // Text areas
+ textarea {
+ max-width: 100%;
+
+ &[rows] {
+ height: auto;
+ }
+ }
+
+ input,
+ textarea {
+ // Disabled/readonly state
+ &:disabled,
+ &[readonly] {
+ background-color: $input-background-disabled;
+ cursor: $input-cursor-disabled;
+ }
+ }
+
+ // Reset styles on button-like inputs
+ [type='submit'],
+ [type='button'] {
+ appearance: none;
+ border-radius: $form-button-radius;
+ }
+
+ // Reset Normalize setting content-box to search elements
+ input[type='search'] { // sass-lint:disable-line no-qualifying-elements
+ box-sizing: border-box;
+ }
+
+ // Number input styles
+ [type='number'] {
+ @if not $input-number-spinners {
+ -moz-appearance: textfield; // sass-lint:disable-line no-vendor-prefixes
+
+ &::-webkit-inner-spin-button,
+ &::-webkit-outer-spin-button {
+ -webkit-appearance: none; // sass-lint:disable-line no-vendor-prefixes
+ margin: 0;
+ }
+ }
+ }
+
+ // Placeholder text
+ ::placeholder {
+ color: $input-placeholder-color;
+ }
+}
diff --git a/src/foundation/foundation.scss b/src/foundation/foundation.scss
new file mode 100644
index 0000000..a639583
--- /dev/null
+++ b/src/foundation/foundation.scss
@@ -0,0 +1,155 @@
+/**
+ * Foundation for Sites by ZURB
+ * Version 6.6.1
+ * foundation.zurb.com
+ * Licensed under MIT Open Source
+ */
+
+// --- Dependencies ---
+@import 'vendor/normalize';
+@import '../_vendor/sassy-lists/stylesheets/helpers/missing-dependencies';
+@import '../_vendor/sassy-lists/stylesheets/helpers/true';
+@import '../_vendor/sassy-lists/stylesheets/functions/contain';
+@import '../_vendor/sassy-lists/stylesheets/functions/purge';
+@import '../_vendor/sassy-lists/stylesheets/functions/remove';
+@import '../_vendor/sassy-lists/stylesheets/functions/replace';
+@import '../_vendor/sassy-lists/stylesheets/functions/to-list';
+
+// --- Settings ---
+// import your own `settings` here or
+// import and modify the default settings through
+@import '_settings';
+
+// --- Components ---
+// Utilities
+@import 'util/util';
+// Global styles
+@import 'global';
+@import 'forms/forms';
+@import 'typography/typography';
+
+// Grids
+@import 'grid/grid';
+@import 'xy-grid/xy-grid';
+// Generic components
+@import 'components/button';
+@import 'components/button-group';
+@import 'components/close-button';
+@import 'components/label';
+@import 'components/progress-bar';
+@import 'components/slider';
+@import 'components/switch';
+@import 'components/table';
+// Basic components
+@import 'components/badge';
+@import 'components/breadcrumbs';
+@import 'components/callout';
+@import 'components/card';
+@import 'components/dropdown';
+@import 'components/pagination';
+@import 'components/tooltip';
+
+// Containers
+@import 'components/accordion';
+@import 'components/media-object';
+@import 'components/orbit';
+@import 'components/responsive-embed';
+@import 'components/tabs';
+@import 'components/thumbnail';
+// Menu-based containers
+@import 'components/menu';
+@import 'components/menu-icon';
+@import 'components/accordion-menu';
+@import 'components/drilldown';
+@import 'components/dropdown-menu';
+
+// Layout components
+@import 'components/off-canvas';
+@import 'components/reveal';
+@import 'components/sticky';
+@import 'components/title-bar';
+@import 'components/top-bar';
+
+// Helpers
+@import 'components/float';
+@import 'components/flex';
+@import 'components/visibility';
+@import 'prototype/prototype';
+
+
+@mixin foundation-everything(
+ $flex: true,
+ $prototype: false,
+ $xy-grid: $xy-grid
+) {
+ @if $flex {
+ $global-flexbox: true !global;
+ }
+
+ @if $xy-grid {
+ $xy-grid: true !global;
+ }
+
+ // Global styles
+ @include foundation-global-styles;
+ @include foundation-forms;
+ @include foundation-typography;
+
+ // Grids
+ @if not $flex {
+ @include foundation-grid;
+ }
+ @else {
+ @if $xy-grid {
+ @include foundation-xy-grid-classes;
+ }
+ @else {
+ @include foundation-flex-grid;
+ }
+ }
+
+ // Generic components
+ @include foundation-button;
+ @include foundation-button-group;
+ @include foundation-close-button;
+ @include foundation-label;
+ @include foundation-progress-bar;
+ @include foundation-slider;
+ @include foundation-switch;
+ @include foundation-table;
+ // Basic components
+ @include foundation-badge;
+ @include foundation-breadcrumbs;
+ @include foundation-callout;
+ @include foundation-card;
+ @include foundation-dropdown;
+ @include foundation-pagination;
+ @include foundation-tooltip;
+
+ // Containers
+ @include foundation-accordion;
+ @include foundation-media-object;
+ @include foundation-orbit;
+ @include foundation-responsive-embed;
+ @include foundation-tabs;
+ @include foundation-thumbnail;
+ // Menu-based containers
+ @include foundation-menu;
+ @include foundation-menu-icon;
+ @include foundation-accordion-menu;
+ @include foundation-drilldown-menu;
+ @include foundation-dropdown-menu;
+
+ // Layout components
+ @include foundation-off-canvas;
+ @include foundation-reveal;
+ @include foundation-sticky;
+ @include foundation-title-bar;
+ @include foundation-top-bar;
+
+ // Helpers
+ @include foundation-float-classes;
+ @if $flex { @include foundation-flex-classes; }
+ @include foundation-visibility-classes;
+ @if $prototype { @include foundation-prototype-classes; }
+}
diff --git a/src/foundation/grid/_classes.scss b/src/foundation/grid/_classes.scss
new file mode 100644
index 0000000..3469aa3
--- /dev/null
+++ b/src/foundation/grid/_classes.scss
@@ -0,0 +1,189 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group grid
+////
+
+/// Outputs CSS classes for the grid.
+/// @access private
+@mixin foundation-grid(
+ $row: 'row',
+ $column: 'column',
+ $column-row: 'column-row',
+ $gutter: 'gutter',
+ $push: 'push',
+ $pull: 'pull',
+ $center: 'centered',
+ $uncenter: 'uncentered',
+ $collapse: 'collapse',
+ $uncollapse: 'uncollapse',
+ $offset: 'offset',
+ $end: 'end',
+ $expanded: 'expanded',
+ $block: 'block'
+) {
+ // Row
+ .#{$row} {
+ @include grid-row;
+
+ // Collapsing
+ &.#{$collapse} {
+ > .#{$column} {
+ @include grid-col-collapse;
+ }
+ }
+
+ // Nesting
+ & .#{$row} {
+ @include grid-row-nest($grid-column-gutter);
+
+ &.#{$collapse} {
+ margin-right: 0;
+ margin-left: 0;
+ }
+ }
+
+ // Expanded (full-width) row
+ &.#{$expanded} {
+ @include grid-row-size(expand);
+
+ .#{$row} {
+ margin-right: auto;
+ margin-left: auto;
+ }
+ }
+
+ &:not(.#{$expanded}) .#{$row} {
+ @include grid-row-size(expand);
+ }
+
+ @if type-of($grid-column-gutter) == 'map' {
+ // Static (unresponsive) row gutters
+ //
+ @each $breakpoint, $value in $grid-column-gutter {
+ &.#{$gutter}-#{$breakpoint} {
+ > .#{$column} {
+ @include grid-col-gutter($value);
+ }
+ }
+ }
+ }
+ }
+
+ // Column
+ .#{$column} {
+ @include grid-col;
+
+ @if $grid-column-align-edge {
+ &.#{$end} {
+ @include grid-col-end;
+ }
+ }
+ }
+
+ // Column row
+ // The double .row class is needed to bump up the specificity
+ .#{$column}.#{$row}.#{$row} {
+ float: none;
+ }
+
+ // To properly nest a column row, padding and margin is removed
+ .#{$row} .#{$column}.#{$row}.#{$row} {
+ margin-right: 0;
+ margin-left: 0;
+ padding-right: 0;
+ padding-left: 0;
+ }
+
+ @include -zf-each-breakpoint {
+ @for $i from 1 through $grid-column-count {
+ // Column width
+ .#{$-zf-size}-#{$i} {
+ @include grid-col-size($i);
+ }
+
+ // Source ordering
+ @if $i < $grid-column-count {
+ @if $push {
+ .#{$-zf-size}-#{$push}-#{$i} {
+ @include grid-col-pos($i);
+ }
+ }
+
+ @if $pull {
+ .#{$-zf-size}-#{$pull}-#{$i} {
+ @include grid-col-pos(-$i);
+ }
+ }
+ }
+
+ // Offsets
+ $o: $i - 1;
+
+ @if $offset {
+ .#{$-zf-size}-#{$offset}-#{$o} {
+ @include grid-col-off($o);
+ }
+ }
+ }
+
+ // Block grid
+ @for $i from 1 through $block-grid-max {
+ .#{$-zf-size}-up-#{$i} {
+ @include grid-layout($i, '.#{$column}');
+ }
+ }
+
+ // Responsive collapsing
+ .#{$-zf-size}-#{$collapse} {
+ > .#{$column} { @include grid-col-collapse; }
+
+ .#{$row} {
+ margin-right: 0;
+ margin-left: 0;
+ }
+ }
+
+ .#{$expanded}.#{$row} .#{$-zf-size}-#{$collapse}.#{$row} {
+ margin-right: 0;
+ margin-left: 0;
+ }
+
+ .#{$-zf-size}-#{$uncollapse} {
+ > .#{$column} { @include grid-col-gutter($-zf-size); }
+ }
+
+ // Positioning
+ @if $center {
+ .#{$-zf-size}-#{$center} {
+ @include grid-col-pos(center);
+ }
+ }
+
+ // Gutter adjustment
+ $-gutter-unpos-selector: (
+ if($uncenter, '.#{$-zf-size}-#{$uncenter}', null),
+ if($push, '.#{$-zf-size}-#{$push}-0', null),
+ if($pull, '.#{$-zf-size}-#{$pull}-0', null),
+ );
+ @if ($uncenter or $push or $pull) {
+ #{$-gutter-unpos-selector} {
+ @include grid-col-unpos;
+ }
+ }
+ }
+
+ // Block grid columns
+ .#{$column}-#{$block} {
+ @include grid-column-margin;
+ }
+
+ @if $column == 'column' and has-value($grid-column-alias) {
+ .#{$grid-column-alias} {
+ // sass-lint:disable-block placeholder-in-extend
+ @extend .column;
+ }
+ }
+}
diff --git a/src/foundation/grid/_column.scss b/src/foundation/grid/_column.scss
new file mode 100644
index 0000000..26c8d15
--- /dev/null
+++ b/src/foundation/grid/_column.scss
@@ -0,0 +1,78 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group grid
+////
+
+/// Calculates the width of a column based on a number of factors.
+///
+/// @param {Number|List} $columns
+/// Width of the column. Accepts multiple values:
+/// - A percentage value will make the column that exact size.
+/// - A single digit will make the column span that number of columns wide, taking into account the column count of the parent row.
+/// - A list of the format "x of y" (without quotes) will make a column that is *x* columns wide, assuming *y* total columns for the parent.
+///
+/// @returns {Number} A calculated percentage value.
+@function grid-column($columns) {
+ @return fraction-to-percentage($columns, $denominator: $grid-column-count);
+}
+
+/// Creates a grid column.
+///
+/// @param {Mixed} $columns [$grid-column-count] - Width of the column. Refer to the `grid-column()` function to see possible values.
+/// @param {Mixed} $gutters [$grid-column-gutter] - Spacing between columns. Refer to the `grid-column-gutter()` function to see possible values.
+@mixin grid-column(
+ $columns: $grid-column-count,
+ $gutters: $grid-column-gutter
+) {
+ @include grid-column-size($columns);
+ float: $global-left;
+
+ // Gutters
+ @include grid-column-gutter($gutters: $gutters);
+
+ // Position
+ @include grid-col-pos(auto);
+}
+
+/// Creates a grid column row. This is the equivalent of adding `.row` and `.column` to the same element.
+///
+/// @param {Mixed} $gutters [$grid-column-gutter] - Width of the gutters on either side of the column row. Refer to the `grid-column-gutter()` function to see possible values.
+@mixin grid-column-row(
+ $gutters: $grid-column-gutter
+) {
+ @include grid-row;
+ @include grid-column($gutters: $gutters);
+
+ &,
+ &:last-child {
+ float: none;
+ }
+}
+
+/// Shorthand for `grid-column()`.
+/// @alias grid-column
+@function grid-col(
+ $columns: $grid-column-count
+) {
+ @return grid-column($columns);
+}
+
+/// Shorthand for `grid-column()`.
+/// @alias grid-column
+@mixin grid-col(
+ $columns: $grid-column-count,
+ $gutters: $grid-column-gutter
+) {
+ @include grid-column($columns, $gutters);
+}
+
+/// Shorthand for `grid-column-row()`.
+/// @alias grid-column-row
+@mixin grid-col-row(
+ $gutters: $grid-column-gutter
+) {
+ @include grid-column-row($gutters);
+}
diff --git a/src/foundation/grid/_flex-grid.scss b/src/foundation/grid/_flex-grid.scss
new file mode 100644
index 0000000..d3d70f9
--- /dev/null
+++ b/src/foundation/grid/_flex-grid.scss
@@ -0,0 +1,260 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group flex-grid
+////
+
+/// Creates a container for a flex grid row.
+///
+/// @param {Keyword|List} $behavior [null]
+/// Modifications to the default grid styles. `nest` indicates the row will be placed inside another row. `collapse` indicates that the columns inside this row will not have padding. `nest collapse` combines both behaviors.
+/// @param {Keyword|Number} $size [$grid-row-width] Maximum size of the row. Set to `expand` to make the row taking the full width.
+/// @param {Number} $columns [null] - Number of columns to use for this row. If set to `null` (the default), the global column count will be used.
+/// @param {Boolean} $base [true] - Set to `false` to prevent basic styles from being output. Useful if you're calling this mixin on the same element twice, as it prevents duplicate CSS output.
+/// @param {Boolean} $wrap [true] - Set to `false` to have row wrapping behavior set to nowrap
+/// @param {Number|Map} $gutters [$grid-column-gutter] - Gutter map or single value to use when inverting margins, in case the row is nested. Responsive gutter settings by default.
+@mixin flex-grid-row(
+ $behavior: null,
+ $size: $grid-row-width,
+ $columns: null,
+ $base: true,
+ $wrap: true,
+ $gutters: $grid-column-gutter
+) {
+ $margin: auto;
+ $wrap: if($wrap, wrap, nowrap);
+
+ @if index($behavior, nest) != null {
+ @include grid-row-nest($gutters);
+
+ @if index($behavior, collapse) != null {
+ margin-right: 0;
+ margin-left: 0;
+ }
+ }
+ @else {
+ @include grid-row-size($size);
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ @if $base {
+ display: flex;
+ flex-flow: row $wrap;
+ }
+
+ @if $columns != null {
+ @include grid-context($columns, $base) {
+ @content;
+ }
+ }
+}
+
+/// Calculates the `flex` property for a flex grid column. It accepts all of the same values as the basic `grid-column()` function, along with two extras:
+/// - `expand` (the default) will make the column expand to fill space.
+/// - `shrink` will make the column contract, so it only takes up the horizontal space it needs.
+///
+/// @param {Mixed} $columns [expand] - Width of the column.
+@function flex-grid-column($columns: expand) {
+ $flex: 1 1 0px; // sass-lint:disable-line zero-unit
+
+ @if $columns == shrink {
+ $flex: 0 0 auto;
+ }
+ @else if $columns != expand {
+ $flex: 0 0 grid-column($columns);
+ }
+
+ @return $flex;
+}
+
+/// Creates a column for a flex grid. By default, the column will stretch to the full width of its container, but this can be overridden with sizing classes, or by using the `unstack` class on the parent flex row.
+///
+/// @param {Mixed} $columns [expand] - Width of the column. Refer to the `flex-grid-column()` function to see possible values.
+/// @param {Number|Map} $gutters [$grid-column-gutter] - Map or single value for gutters width. See the `grid-column-gutter` mixin.
+@mixin flex-grid-column(
+ $columns: expand,
+ $gutters: $grid-column-gutter
+) {
+ // Base properties
+ @include flex-grid-size($columns);
+
+ // Gutters
+ @include grid-column-gutter($gutters: $gutters);
+
+ // fixes recent Chrome version not limiting child width
+ // https://stackoverflow.com/questions/34934586/white-space-nowrap-and-flexbox-did-not-work-in-chrome
+ @if $columns == expand {
+ min-width: 0;
+ }
+}
+
+/// Creates a block grid for a flex grid row.
+///
+/// @param {Number} $n - Number of columns to display on each row.
+/// @param {String} $selector - Selector to use to target columns within the row.
+@mixin flex-grid-layout(
+ $n,
+ $selector: '.column'
+) {
+ flex-wrap: wrap;
+
+ > #{$selector} {
+ $pct: percentage(1/$n);
+
+ flex: 0 0 $pct;
+ max-width: $pct;
+ }
+}
+
+/// Changes the width flex grid column.
+/// @param {Mixed} $columns [expand] - Width of the column. Refer to the `flex-grid-column()` function to see possible values.
+@mixin flex-grid-size($columns: null) {
+ $columns: $columns or expand;
+
+ flex: flex-grid-column($columns);
+
+ // max-width fixes IE 10/11 not respecting the flex-basis property
+ @if $columns != expand and $columns != shrink {
+ max-width: grid-column($columns);
+ }
+}
+
+
+@mixin foundation-flex-grid {
+ // Row
+ .row {
+ @include flex-grid-row;
+
+ // Nesting behavior
+ & .row {
+ @include flex-grid-row(nest, $base: false);
+
+ &.collapse {
+ margin-right: 0;
+ margin-left: 0;
+ }
+ }
+
+ // Expanded row
+ &.expanded {
+ @include grid-row-size(expand);
+
+ .row {
+ margin-right: auto;
+ margin-left: auto;
+ }
+ }
+
+ &:not(.expanded) .row {
+ @include grid-row-size(expand);
+ }
+
+ &.collapse {
+ > .column {
+ @include grid-col-collapse;
+ }
+ }
+
+ // Undo negative margins
+ // From collapsed child
+ &.is-collapse-child,
+ &.collapse > .column > .row {
+ margin-right: 0;
+ margin-left: 0;
+ }
+ }
+
+ // Column
+ .column {
+ @include flex-grid-column;
+ }
+
+ // Column row
+ // The double .row class is needed to bump up the specificity
+ .column.row.row {
+ float: none;
+ display: block;
+ }
+
+ // To properly nest a column row, padding and margin is removed
+ .row .column.row.row {
+ margin-right: 0;
+ margin-left: 0;
+ padding-right: 0;
+ padding-left: 0;
+ }
+
+ @include -zf-each-breakpoint {
+ @for $i from 1 through $grid-column-count {
+ // Sizing (percentage)
+ .#{$-zf-size}-#{$i} {
+ flex: flex-grid-column($i);
+ max-width: grid-column($i);
+ }
+
+ // Offsets
+ $o: $i - 1;
+
+ .#{$-zf-size}-offset-#{$o} {
+ @include grid-column-offset($o);
+ }
+ }
+
+ // Block grid
+ @for $i from 1 through $block-grid-max {
+ .#{$-zf-size}-up-#{$i} {
+ @include flex-grid-layout($i);
+ }
+ }
+
+ @if $-zf-size != $-zf-zero-breakpoint {
+ // Sizing (expand)
+ @include breakpoint($-zf-size) {
+ .#{$-zf-size}-expand {
+ flex: flex-grid-column();
+ }
+ }
+
+ // Auto-stacking/unstacking
+ @at-root (without: media) {
+ .row.#{$-zf-size}-unstack {
+ > .column {
+ flex: flex-grid-column(100%);
+
+ @include breakpoint($-zf-size) {
+ flex: flex-grid-column();
+ }
+ }
+ }
+ }
+ }
+
+ // Responsive collapsing
+ .#{$-zf-size}-collapse {
+ > .column { @include grid-col-collapse; }
+ }
+
+ .#{$-zf-size}-uncollapse {
+ > .column { @include grid-col-gutter($-zf-size); }
+ }
+ }
+
+ // Sizing (shrink)
+ .shrink {
+ flex: flex-grid-column(shrink);
+ max-width: 100%;
+ }
+
+ // Block grid columns
+ .column-block {
+ @include grid-column-margin;
+ }
+
+ .columns {
+ @extend .column; // sass-lint:disable-line placeholder-in-extend
+
+ }
+}
diff --git a/src/foundation/grid/_grid.scss b/src/foundation/grid/_grid.scss
new file mode 100644
index 0000000..34b2b50
--- /dev/null
+++ b/src/foundation/grid/_grid.scss
@@ -0,0 +1,48 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group grid
+////
+
+/// The maximum width of a row.
+/// @type Number
+$grid-row-width: $global-width !default;
+
+/// The default column count of a grid. Changing this value affects the logic of the grid mixins, and the number of CSS classes output.
+/// @type Number
+$grid-column-count: 12 !default;
+
+/// The amount of space between columns at different screen sizes. To use just one size, set the variable to a number instead of a map.
+/// @type Map | Length
+/// @since 6.1.0
+$grid-column-gutter: (
+ small: 20px,
+ medium: 30px,
+) !default;
+
+/// If `true`, the last column in a row will align to the opposite edge of the row.
+/// @type Boolean
+$grid-column-align-edge: true !default;
+
+/// Selector used for an alias of column (with @extend). If `false`, no alias is created.
+/// @type String
+$grid-column-alias: 'columns' !default;
+
+/// The highest number of `.x-up` classes available when using the block grid CSS.
+/// @type Number
+$block-grid-max: 8 !default;
+
+// Internal value to store the end column float direction
+$-zf-end-float: if($grid-column-align-edge, $global-right, $global-left);
+
+@import 'row';
+@import 'column';
+@import 'size';
+@import 'position';
+@import 'gutter';
+@import 'classes';
+@import 'layout';
+
+@import 'flex-grid';
diff --git a/src/foundation/grid/_gutter.scss b/src/foundation/grid/_gutter.scss
new file mode 100644
index 0000000..2b9ba3f
--- /dev/null
+++ b/src/foundation/grid/_gutter.scss
@@ -0,0 +1,67 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group grid
+////
+
+/// Set the gutters on a column
+/// @param {Number|Keyword} $gutter [auto]
+/// Spacing between columns, accepts multiple values:
+/// - A single value will make the gutter that exact size.
+/// - A breakpoint name will make the gutter the corresponding size in the $gutters map.
+/// - "auto" will make the gutter responsive, using the $gutters map values.
+/// @param {Number|Map} $gutters [$grid-column-gutter] - Gutter map or single value to use. Responsive gutter settings by default.
+@mixin grid-column-gutter(
+ $gutter: auto,
+ $gutters: $grid-column-gutter
+) {
+ @include -zf-breakpoint-value($gutter, $gutters) {
+ $padding: rem-calc($-zf-bp-value) / 2;
+
+ padding-right: $padding;
+ padding-left: $padding;
+ }
+}
+
+/// Collapse the gutters on a column by removing the padding. **Note:** only use this mixin within a breakpoint. To collapse a column's gutters on all screen sizes, use the `$gutter` parameter of the `grid-column()` mixin instead.
+@mixin grid-column-collapse {
+ @include grid-column-gutter(0);
+}
+
+/// Shorthand for `grid-column-gutter()`.
+/// @alias grid-column-gutter
+@mixin grid-col-gutter(
+ $gutter: auto,
+ $gutters: $grid-column-gutter
+) {
+ @include grid-column-gutter($gutter, $gutters);
+}
+
+/// Shorthand for `grid-column-collapse()`.
+/// @alias grid-column-collapse
+@mixin grid-col-collapse {
+ @include grid-column-collapse;
+}
+
+/// Sets bottom margin on grid columns to match gutters
+/// @param {Number|Keyword} $margin [auto]
+/// The bottom margin on grid columns, accepts multiple values:
+/// - A single value will make the margin that exact size.
+/// - A breakpoint name will make the margin the corresponding size in the $margins map.
+/// - "auto" will make the margin responsive, using the $margins map values.
+/// @param {Number|Map} $margins [$grid-column-gutter] - Map or single value to use. Responsive gutter settings by default.
+@mixin grid-column-margin (
+ $margin: auto,
+ $margins: $grid-column-gutter
+) {
+ @include -zf-breakpoint-value($margin, $margins) {
+ $margin-bottom: rem-calc($-zf-bp-value);
+ margin-bottom: $margin-bottom;
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+ }
+}
diff --git a/src/foundation/grid/_layout.scss b/src/foundation/grid/_layout.scss
new file mode 100644
index 0000000..1e08791
--- /dev/null
+++ b/src/foundation/grid/_layout.scss
@@ -0,0 +1,76 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group grid
+////
+
+/// Sizes child elements so that `$n` number of items appear on each row.
+///
+/// @param {Number} $n - Number of elements to display per row.
+/// @param {String} $selector ['.column'] - Selector(s) to use for child elements.
+/// @param {Number|List} $gutter
+/// The gutter to apply to child elements. Accepts multiple values:
+/// - $grid-column-gutter will use the values in the $grid-column-gutter map, including breakpoint sizes.
+/// - A fixed numeric value will apply this gutter to all breakpoints.
+@mixin grid-layout(
+ $n,
+ $selector: '.column',
+ $gutter: null
+) {
+ & > #{$selector} {
+ float: $global-left;
+ width: percentage(1/$n);
+
+ // If a $gutter value is passed
+ @if($gutter) {
+ // Gutters
+ @if type-of($gutter) == 'map' {
+ @each $breakpoint, $value in $gutter {
+ $padding: rem-calc($value) / 2;
+
+ @include breakpoint($breakpoint) {
+ padding-right: $padding;
+ padding-left: $padding;
+ }
+ }
+ }
+ @else if type-of($gutter) == 'number' and strip-unit($gutter) > 0 {
+ $padding: rem-calc($gutter) / 2;
+ padding-right: $padding;
+ padding-left: $padding;
+ }
+ }
+
+ &:nth-of-type(1n) {
+ clear: none;
+ }
+
+ &:nth-of-type(#{$n}n+1) {
+ clear: both;
+ }
+
+ &:last-child {
+ float: $global-left;
+ }
+ }
+}
+
+/// Adds extra CSS to block grid children so the last items in the row center automatically. Apply this to the columns, not the row.
+///
+/// @param {Number} $n - Number of items that appear in each row.
+@mixin grid-layout-center-last($n) {
+ @for $i from 1 to $n {
+ @if $i == 1 {
+ &:nth-child(#{$n}n+1):last-child {
+ margin-left: (100 - 100/$n * $i) / 2 * 1%;
+ }
+ }
+ @else {
+ &:nth-child(#{$n}n+1):nth-last-child(#{$i}) {
+ margin-left: (100 - 100/$n * $i) / 2 * 1%;
+ }
+ }
+ }
+}
diff --git a/src/foundation/grid/_position.scss b/src/foundation/grid/_position.scss
new file mode 100644
index 0000000..24b49dd
--- /dev/null
+++ b/src/foundation/grid/_position.scss
@@ -0,0 +1,100 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group grid
+////
+
+/// Reposition a column.
+///
+/// @param {Number|Keyword} $position - It can be:
+/// * A number: The column will move equal to the width of the column count
+/// specified. A positive number will push the column to the right, while
+/// a negative number will pull it to the left.
+/// * `center`: Column will be centered
+/// * `auto`: Column will be pushed to the left (or to the right for the last column).
+@mixin grid-column-position($position) {
+ // Auto positioning
+ @if $position == auto {
+ &, &:last-child:not(:first-child) {
+ float: $global-left;
+ clear: none;
+ }
+
+ // Last column alignment
+ @if $grid-column-align-edge {
+ &:last-child:not(:first-child) {
+ float: $global-right;
+ }
+ }
+ }
+
+ // Push/pull
+ @else if type-of($position) == 'number' {
+ $offset: percentage($position / $grid-column-count);
+
+ position: relative;
+ #{$global-left}: $offset;
+ }
+
+ // Center positioning
+ @else if $position == center {
+ &, &:last-child:not(:first-child) {
+ float: none;
+ clear: both;
+ }
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ @else {
+ @warn 'Wrong syntax for grid-column-position(). Enter a positive or negative number, "center" or "auto".';
+ }
+}
+
+/// Reset a position definition.
+@mixin grid-column-unposition {
+ @include grid-column-position(auto);
+ position: static;
+ margin-right: 0;
+ margin-left: 0;
+}
+
+/// Offsets a column to the right by `$n` columns.
+/// @param {Number|List} $n - Width to offset by. You can pass in any value accepted by the `grid-column()` mixin, such as `6`, `50%`, or `1 of 2`.
+@mixin grid-column-offset($n) {
+ margin-#{$global-left}: grid-column($n);
+}
+
+/// Disable the default behavior of the last column in a row aligning to the opposite edge.
+@mixin grid-column-end {
+ // This extra specificity is required for the property to be applied
+ &:last-child:last-child {
+ float: $global-left;
+ }
+}
+
+/// Shorthand for `grid-column-position()`.
+/// @alias grid-column-position
+@mixin grid-col-pos($position) {
+ @include grid-column-position($position);
+}
+
+/// Shorthand for `grid-column-unposition()`.
+/// @alias grid-column-unposition
+@mixin grid-col-unpos {
+ @include grid-column-unposition;
+}
+
+/// Shorthand for `grid-column-offset()`.
+/// @alias grid-column-offset
+@mixin grid-col-off($n) {
+ @include grid-column-offset($n);
+}
+
+/// Shorthand for `grid-column-end()`.
+/// @alias grid-column-end
+@mixin grid-col-end {
+ @include grid-column-end;
+}
diff --git a/src/foundation/grid/_row.scss b/src/foundation/grid/_row.scss
new file mode 100644
index 0000000..e971855
--- /dev/null
+++ b/src/foundation/grid/_row.scss
@@ -0,0 +1,99 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group grid
+////
+
+/// Change the behavior of columns defined inside this mixin to use a different column count.
+/// @content
+///
+/// @param {Number} $columns - Number of columns to use.
+/// @param {Boolean} $root [false]
+/// If `false`, selectors inside this mixin will nest inside the parent selector.
+/// If `true`, selectors will not nest.
+@mixin grid-context(
+ $columns,
+ $root: false
+) {
+ // Store the current column count so it can be re-set later
+ $old-grid-column-count: $grid-column-count;
+ $grid-column-count: $columns !global;
+
+ @if $root {
+ @at-root { @content; }
+ }
+ @else {
+ @content;
+ }
+
+ // Restore the old column count
+ $grid-column-count: $old-grid-column-count !global;
+}
+
+/// Creates a grid row.
+/// @content
+///
+/// @param {Number} $columns [null] - Column count for this row. `null` will use the default column count.
+/// @param {Keywords} $behavior [null]
+/// Modifications to the default grid styles. `nest` indicates the row will be placed inside another row. `collapse` indicates that the columns inside this row will not have padding. `nest collapse` combines both behaviors.
+/// @param {Keyword|Number} $size [$grid-row-width] Maximum size of the row. Set to `expand` to make the row taking the full width.
+/// @param {Boolean} $cf [true] - Whether or not to include a clearfix.
+/// @param {Number|Map} $gutters [$grid-column-gutter] - Gutter map or single value to use when inverting margins. Responsive gutter settings by default.
+@mixin grid-row(
+ $columns: null,
+ $behavior: null,
+ $size: $grid-row-width,
+ $cf: true,
+ $gutters: $grid-column-gutter
+) {
+ $margin: auto;
+
+ @if index($behavior, nest) != null {
+ @include grid-row-nest($gutters);
+
+ @if index($behavior, collapse) != null {
+ margin-right: 0;
+ margin-left: 0;
+ }
+ }
+ @else {
+ @include grid-row-size($size);
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ @if $cf {
+ @include clearfix;
+ }
+
+ @if $columns != null {
+ @include grid-context($columns) {
+ @content;
+ }
+ }
+}
+
+/// Inverts the margins of a row to nest it inside of a column.
+///
+/// @param {Number|Map} $gutters [$grid-column-gutter] - Gutter map or single value to use when inverting margins. Responsive gutter settings by default.
+@mixin grid-row-nest($gutters: $grid-column-gutter) {
+ @include -zf-each-breakpoint {
+ $margin: rem-calc(-zf-get-bp-val($gutters, $-zf-size)) / 2 * -1;
+
+ margin-right: $margin;
+ margin-left: $margin;
+ }
+}
+
+/// Set a grid row size
+///
+/// @param {Keyword|Number} $size [$grid-row-width] Maximum size of the row. Set to `expand` to make the row taking the full width.
+@mixin grid-row-size($size: $grid-row-width) {
+ @if $size == expand {
+ $size: none;
+ }
+
+ max-width: $size;
+}
diff --git a/src/foundation/grid/_size.scss b/src/foundation/grid/_size.scss
new file mode 100644
index 0000000..c01c8ca
--- /dev/null
+++ b/src/foundation/grid/_size.scss
@@ -0,0 +1,24 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group grid
+////
+
+/// Set the width of a grid column.
+///
+/// @param {Number|List} $width [$grid-column-count] - Width to make the column. You can pass in any value accepted by the `grid-column()` function, such as `6`, `50%`, or `1 of 2`.
+@mixin grid-column-size(
+ $columns: $grid-column-count
+) {
+ width: grid-column($columns);
+}
+
+/// Shorthand for `grid-column-size()`.
+/// @alias grid-column-size
+@mixin grid-col-size(
+ $columns: $grid-column-count
+) {
+ @include grid-column-size($columns);
+}
diff --git a/src/foundation/prototype/_arrow.scss b/src/foundation/prototype/_arrow.scss
new file mode 100644
index 0000000..3ba57c2
--- /dev/null
+++ b/src/foundation/prototype/_arrow.scss
@@ -0,0 +1,36 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-arrow
+////
+
+/// Map containing all the `arrow` direction
+/// @type Map
+$prototype-arrow-directions: (
+ down,
+ up,
+ right,
+ left
+) !default;
+
+/// Width of the Arrow, `0.4375rem` by default.
+/// @type Number
+$prototype-arrow-size: 0.4375rem;
+
+/// Color of the Arrow, `$black` by default.
+/// @type Color
+$prototype-arrow-color: $black;
+
+@mixin foundation-prototype-arrow {
+ @each $prototype-arrow-direction in $prototype-arrow-directions {
+ .arrow-#{$prototype-arrow-direction} {
+ @include css-triangle(
+ $prototype-arrow-size,
+ $prototype-arrow-color,
+ $prototype-arrow-direction
+ );
+ }
+ }
+}
diff --git a/src/foundation/prototype/_border-box.scss b/src/foundation/prototype/_border-box.scss
new file mode 100644
index 0000000..e04b5f0
--- /dev/null
+++ b/src/foundation/prototype/_border-box.scss
@@ -0,0 +1,35 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-border-box
+////
+
+/// Responsive breakpoints for border box.
+/// @type Boolean
+$prototype-border-box-breakpoints: $global-prototype-breakpoints !default;
+
+/// Border box utility
+@mixin border-box {
+ box-sizing: border-box !important;
+}
+
+@mixin foundation-prototype-border-box {
+ .border-box {
+ @include border-box;
+ }
+
+ @if ($prototype-border-box-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-border-box {
+ @include border-box;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_border-none.scss b/src/foundation/prototype/_border-none.scss
new file mode 100644
index 0000000..5408038
--- /dev/null
+++ b/src/foundation/prototype/_border-none.scss
@@ -0,0 +1,35 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-border-none
+////
+
+/// Responsive breakpoints for border none.
+/// @type Boolean
+$prototype-border-none-breakpoints: $global-prototype-breakpoints !default;
+
+/// Border none utility
+@mixin border-none {
+ border: none !important;
+}
+
+@mixin foundation-prototype-border-none {
+ .border-none {
+ @include border-none;
+ }
+
+ @if ($prototype-border-none-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-border-none {
+ @include border-none;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_bordered.scss b/src/foundation/prototype/_bordered.scss
new file mode 100644
index 0000000..2d4d6ae
--- /dev/null
+++ b/src/foundation/prototype/_bordered.scss
@@ -0,0 +1,54 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-bordered
+////
+
+/// Responsive breakpoints for bordered utility.
+/// @type Boolean
+$prototype-bordered-breakpoints: $global-prototype-breakpoints !default;
+
+/// Default value for `prototype-border-width`
+/// @type Number
+$prototype-border-width: rem-calc(1) !default;
+
+/// Default value for `prototype-border-type`
+/// @type String
+$prototype-border-type: solid !default;
+
+/// Default value for `prototype-border-color` defaulted to `medium-gray`
+/// @type Color
+$prototype-border-color: $medium-gray !default;
+
+/// Bordered Utility: Adds a light border to an element by default.
+/// @param {Number} $width [$prototype-border-width] Width of the border
+/// @param {String} $type [$prototype-border-type] Type of the border
+/// @param {Color} $color [$prototype-border-color] Color of the border
+@mixin bordered(
+ $width: $prototype-border-width,
+ $type: $prototype-border-type,
+ $color: $prototype-border-color
+) {
+ border: $width $type $color;
+}
+
+@mixin foundation-prototype-bordered {
+ .bordered {
+ @include bordered;
+ }
+
+ @if ($prototype-bordered-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-bordered {
+ @include bordered;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_box.scss b/src/foundation/prototype/_box.scss
new file mode 100644
index 0000000..0d4cf8e
--- /dev/null
+++ b/src/foundation/prototype/_box.scss
@@ -0,0 +1,23 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-box
+////
+
+/// Box Mixin: Easily create a square, rectangle or a circle
+/// @param {Number} $width[] Width of the box
+/// @param {Number} $height[$width] Height of the box, defaults to `$width` to easily make a square
+/// @param {Boolean} $circle[false] Makes the box a circle, by default `false`.
+@mixin box(
+ $width,
+ $height: $width,
+ $circle: false
+) {
+ width: $width;
+ height: $height;
+ @if $circle {
+ border-radius: 50% !important;
+ }
+}
diff --git a/src/foundation/prototype/_display.scss b/src/foundation/prototype/_display.scss
new file mode 100644
index 0000000..23f8b5c
--- /dev/null
+++ b/src/foundation/prototype/_display.scss
@@ -0,0 +1,50 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-display
+////
+
+/// Responsive breakpoints for display classes
+/// @type Boolean
+$prototype-display-breakpoints: $global-prototype-breakpoints !default;
+
+/// Map containing all the `display` classes
+/// @type Map
+$prototype-display: (
+ inline,
+ inline-block,
+ block,
+ table,
+ table-cell
+) !default;
+
+/// Display classes, by default coming through a map `$prototype-display`
+/// @param {String} $display [] Display classes
+@mixin display($display) {
+ display: $display !important;
+}
+
+@mixin foundation-prototype-display {
+ @each $display in $prototype-display {
+ .display-#{$display} {
+ @include display($display);
+ }
+ }
+
+ @if ($prototype-display-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @each $display in $prototype-display {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-display-#{$display} {
+ @include display($display);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_font-styling.scss b/src/foundation/prototype/_font-styling.scss
new file mode 100644
index 0000000..cf6f570
--- /dev/null
+++ b/src/foundation/prototype/_font-styling.scss
@@ -0,0 +1,95 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-font-styling
+////
+
+/// Responsive breakpoints for font styling types
+/// @type Boolean
+$prototype-font-breakpoints: $global-prototype-breakpoints !default;
+
+/// Letter spacing for `.font-wide`
+/// @type Number
+$prototype-wide-letter-spacing: rem-calc(4) !default;
+
+/// Default weight for `.font-normal`, defaulted to `global-weight-normal`
+/// @type Number
+$prototype-font-normal: $global-weight-normal !default;
+
+/// Default weight for `.font-bold`, defaulted to `global-weight-bold`
+/// @type Number
+$prototype-font-bold: $global-weight-bold !default;
+
+/// Font wide letter spacing!
+/// @param {Number} $letter-spacing [$prototype-wide-letter-spacing] Wide letter spacing for the font
+@mixin font-wide(
+ $letter-spacing: $prototype-wide-letter-spacing
+) {
+ letter-spacing: $letter-spacing;
+}
+
+/// Font Weight Normal, default value coming through `global-weight-normal`
+/// @param {Number} $weight [$prototype-font-normal] Weight of the font (normal)
+@mixin font-normal(
+ $weight: $prototype-font-normal
+) {
+ font-weight: $weight;
+}
+
+/// Font Weight Bold, default value coming through `global-weight-bold`
+/// @param {Number} $weight [$prototype-font-bold] Weight of the font (bold)
+@mixin font-bold(
+ $weight: $prototype-font-bold
+) {
+ font-weight: $weight;
+}
+
+/// Font Style Italic
+@mixin font-italic {
+ font-style: italic !important;
+}
+
+@mixin foundation-prototype-font-styling {
+ .font-wide{
+ @include font-wide;
+ }
+
+ .font-normal {
+ @include font-normal;
+ }
+
+ .font-bold {
+ @include font-bold;
+ }
+
+ .font-italic {
+ @include font-italic;
+ }
+
+ @if ($prototype-font-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-font-wide{
+ @include font-wide;
+ }
+
+ .#{$size}-font-normal {
+ @include font-normal;
+ }
+
+ .#{$size}-font-bold {
+ @include font-bold;
+ }
+
+ .#{$size}-font-italic {
+ @include font-italic;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_list-style-type.scss b/src/foundation/prototype/_list-style-type.scss
new file mode 100644
index 0000000..c9678c4
--- /dev/null
+++ b/src/foundation/prototype/_list-style-type.scss
@@ -0,0 +1,95 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-list-style-type
+////
+
+/// Responsive breakpoints for list styling types
+/// @type Boolean
+$prototype-list-breakpoints: $global-prototype-breakpoints !default;
+
+/// Map containing all the `style-type-unordered` classes
+/// @type Map
+$prototype-style-type-unordered: (
+ disc,
+ circle,
+ square
+) !default;
+
+/// Map containing all the `style-type-ordered` classes
+/// @type Map
+$prototype-style-type-ordered: (
+ decimal,
+ lower-alpha,
+ lower-latin,
+ lower-roman,
+ upper-alpha,
+ upper-latin,
+ upper-roman
+) !default;
+
+
+/// Style type for unordered Lists, by default coming through a map `$prototype-style-type-unordered`
+/// @param {String} $style-type-unordered [] Style type for unordered Lists
+@mixin style-type-unordered($style-type-unordered) {
+ list-style-type: $style-type-unordered !important;
+}
+
+/// Style type for ordered Lists, by default coming through a map `$prototype-style-type-ordered`
+/// @param {String} $style-type-ordered [] Style type for ordered Lists
+@mixin style-type-ordered($style-type-ordered) {
+ list-style-type: $style-type-ordered !important;
+}
+
+@mixin list-unordered {
+ @each $style-type-unordered in $prototype-style-type-unordered {
+ ul.list-#{$style-type-unordered} {
+ @include style-type-unordered($style-type-unordered);
+ }
+ }
+
+ @if ($prototype-list-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @each $style-type-unordered in $prototype-style-type-unordered {
+ @if $size != $-zf-zero-breakpoint {
+ ul.#{$size}-list-#{$style-type-unordered} {
+ @include style-type-unordered($style-type-unordered);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@mixin list-ordered {
+ @each $style-type-ordered in $prototype-style-type-ordered {
+ ol.list-#{$style-type-ordered} {
+ @include style-type-ordered($style-type-ordered);
+ }
+ }
+
+ @if ($prototype-list-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @each $style-type-ordered in $prototype-style-type-ordered {
+ @if $size != $-zf-zero-breakpoint {
+ ol.#{$size}-list-#{$style-type-ordered} {
+ @include style-type-ordered($style-type-ordered);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@mixin foundation-prototype-list-style-type {
+ @include list-unordered;
+ @include list-ordered;
+}
diff --git a/src/foundation/prototype/_overflow.scss b/src/foundation/prototype/_overflow.scss
new file mode 100644
index 0000000..8ac43d5
--- /dev/null
+++ b/src/foundation/prototype/_overflow.scss
@@ -0,0 +1,72 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-overflow
+////
+
+/// Responsive breakpoints for overflow helper classes
+/// @type Boolean
+$prototype-overflow-breakpoints: $global-prototype-breakpoints !default;
+
+/// Map containing all the `overflow` classes
+/// @type Map
+$prototype-overflow: (
+ visible,
+ hidden,
+ scroll
+) !default;
+
+/// Overflow classes, by default coming through a map `$prototype-overflow`
+/// @param {String} $overflow [] Overflow classes
+@mixin overflow($overflow) {
+ overflow: $overflow !important;
+}
+
+/// Overflow classes on horizontal axis, by default coming through a map `$prototype-overflow`
+/// @param {String} $overflow [] Overflow classes (horizontal axis)
+@mixin overflow-x($overflow) {
+ overflow-x: $overflow !important;
+}
+
+/// Overflow classes on vertical axis, by default coming through a map `$prototype-overflow`
+/// @param {String} $overflow [] Overflow classes (vertical axis)
+@mixin overflow-y($overflow) {
+ overflow-y: $overflow !important;
+}
+
+@mixin foundation-prototype-overflow {
+ @each $overflow in $prototype-overflow {
+ .overflow-#{$overflow} {
+ @include overflow($overflow);
+ }
+ .overflow-x-#{$overflow} {
+ @include overflow-x($overflow);
+ }
+ .overflow-y-#{$overflow} {
+ @include overflow-y($overflow);
+ }
+ }
+
+ @if ($prototype-overflow-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @each $overflow in $prototype-overflow {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-overflow-#{$overflow} {
+ @include overflow($overflow);
+ }
+ .#{$size}-overflow-x-#{$overflow} {
+ @include overflow-x($overflow);
+ }
+ .#{$size}-overflow-y-#{$overflow} {
+ @include overflow-y($overflow);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_position.scss b/src/foundation/prototype/_position.scss
new file mode 100644
index 0000000..db01507
--- /dev/null
+++ b/src/foundation/prototype/_position.scss
@@ -0,0 +1,114 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-position
+////
+
+/// Responsive breakpoints for position helpers
+/// @type Boolean
+$prototype-position-breakpoints: $global-prototype-breakpoints !default;
+
+/// Map containing all the `position` classes
+/// @type Map
+$prototype-position: (
+ static,
+ relative,
+ absolute,
+ fixed
+) !default;
+
+/// z-index for fixed positioning
+/// @type Number
+$prototype-position-z-index: 975 !default;
+
+/// Position classes, by default coming through a map `$prototype-position`, whereas all the offset values are multiplied by `$global-position` which by default is equal to `1rem`.
+/// @param {String} $position [] Position classes, Either `static`, `relative`, `absolute` or `fixed`
+/// @param {Number} $top [null] - Top offset
+/// @param {Number} $right [null] - Right offset
+/// @param {Number} $bottom [null] - Bottom offset
+/// @param {Number} $left [null] - Left offset
+@mixin position(
+ $position,
+ $top: null,
+ $right: null,
+ $bottom: null,
+ $left: null
+) {
+ position: $position !important;
+ @if $top != null {
+ top: $top * $global-position !important;
+ }
+ @if $right != null {
+ right: $right * $global-position !important;
+ }
+ @if $bottom != null {
+ bottom: $bottom * $global-position !important;
+ }
+ @if $left != null {
+ left: $left * $global-position !important;
+ }
+}
+
+/// Position Fixed on top corners
+/// @param {Number} $z-index [$prototype-position-z-index] z-index for `position-fixed-top`
+@mixin position-fixed-top(
+ $z-index: $prototype-position-z-index
+) {
+ @include position(fixed, 0, 0, null, 0);
+ z-index: $z-index;
+}
+
+/// Position Fixed on bottom corners
+/// @param {Number} $z-index [$prototype-position-z-index] z-index for `position-fixed-bottom`
+@mixin position-fixed-bottom(
+ $z-index: $prototype-position-z-index
+) {
+ @include position(fixed, null, 0, 0, 0);
+ z-index: $z-index;
+}
+
+@mixin foundation-prototype-position {
+ // Position: Static, Relative, Fixed, Absolute
+ @each $position in $prototype-position {
+ .position-#{$position} {
+ @include position($position);
+ }
+ }
+
+ // Position: Fixed Top, Fixed Bottom
+ .position-fixed-top {
+ @include position-fixed-top;
+ }
+ .position-fixed-bottom {
+ @include position-fixed-bottom;
+ }
+
+ @if ($prototype-position-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ // Position: Static, Relative, Fixed, Absolute
+ @each $position in $prototype-position {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-position-#{$position} {
+ @include position($position);
+ }
+ }
+ }
+
+ // Position: Fixed Top, Fixed Bottom
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-position-fixed-top {
+ @include position-fixed-top;
+ }
+
+ .#{$size}-position-fixed-bottom {
+ @include position-fixed-bottom;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_prototype.scss b/src/foundation/prototype/_prototype.scss
new file mode 100644
index 0000000..c0189db
--- /dev/null
+++ b/src/foundation/prototype/_prototype.scss
@@ -0,0 +1,87 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype
+////
+
+// Relational Mixins
+@import 'relation';
+
+// Box Mixin
+@import 'box';
+
+// Rotate Mixin
+@import 'rotate';
+
+// Text utilities
+@import 'text-utilities';
+
+// Text transformation classes
+@import 'text-transformation';
+
+// Text Decoration classes
+@import 'text-decoration';
+
+// Font Styling
+@import 'font-styling';
+
+// List Style type
+@import 'list-style-type';
+
+// Rounded Utility
+@import 'rounded';
+
+// Bordered Utility
+@import 'bordered';
+
+// Shadow Utility
+@import 'shadow';
+
+// Arrow Utility
+@import 'arrow';
+
+// Separator Utility
+@import 'separator';
+
+// Overflow helper classes
+@import 'overflow';
+
+// Display classes
+@import 'display';
+
+// Position Helpers
+@import 'position';
+
+// Border box
+@import 'border-box';
+
+// Border none Utilty
+@import 'border-none';
+
+// Sizing Utilities
+@import 'sizing';
+
+// Spacing Utilities
+@import 'spacing';
+
+@mixin foundation-prototype-classes {
+ @include foundation-prototype-text-utilities;
+ @include foundation-prototype-text-transformation;
+ @include foundation-prototype-text-decoration;
+ @include foundation-prototype-font-styling;
+ @include foundation-prototype-list-style-type;
+ @include foundation-prototype-rounded;
+ @include foundation-prototype-bordered;
+ @include foundation-prototype-shadow;
+ @include foundation-prototype-arrow;
+ @include foundation-prototype-separator;
+ @include foundation-prototype-overflow;
+ @include foundation-prototype-display;
+ @include foundation-prototype-position;
+ @include foundation-prototype-border-box;
+ @include foundation-prototype-border-none;
+ @include foundation-prototype-sizing;
+ @include foundation-prototype-spacing;
+}
diff --git a/src/foundation/prototype/_relation.scss b/src/foundation/prototype/_relation.scss
new file mode 100644
index 0000000..87a3b35
--- /dev/null
+++ b/src/foundation/prototype/_relation.scss
@@ -0,0 +1,157 @@
+/// Select all children from the first to `$num`.
+/// @param {Number} $num[] First `n` numbers of total children
+@mixin first($num) {
+ @if $num == 1 {
+ &:first-child {
+ @content;
+ }
+ } @else {
+ &:nth-child(-n + #{$num}) {
+ @content;
+ }
+ }
+}
+
+/// Select the first exact child
+@mixin first-child {
+ &:first-of-type {
+ @content;
+ }
+}
+
+/// Select all children from the last to `$num`.
+/// @param {Number} $num[] Last `n` numbers of total children
+@mixin last($num) {
+ &:nth-last-child(-n + #{$num}) {
+ @content;
+ }
+}
+
+/// Select the last exact child
+@mixin last-child {
+ &:last-of-type {
+ @content;
+ }
+}
+
+/// Select children every `$num`.
+/// @param {Number} $num[] Every `n` number of all children
+@mixin every($num) {
+ &:nth-child(#{$num}n) {
+ @content;
+ }
+}
+
+/// Select only the first and last child.
+@mixin first-last {
+ &:first-child,
+ &:last-child {
+ @content;
+ }
+}
+
+/// Select all children after the first to `$num`.
+/// @param {Number} $num[] After First `n` numbers of total children
+@mixin after-first($num) {
+ &:nth-child(n + #{$num + 1}) {
+ @content;
+ }
+}
+
+/// Select all children before `$num` from the last.
+/// @param {Number} $num[] From Last `n` numbers of total children
+@mixin from-last($num) {
+ &:nth-last-child(#{$num}) {
+ @content;
+ }
+}
+
+/// Select the `$num` child from the first and the `$num` child from the last.
+/// @param {Number} $num[] `n` number called from first and last
+@mixin from-first-last($num) {
+ &:nth-child(#{$num}),
+ &:nth-last-child(#{$num}) {
+ @content;
+ }
+}
+
+/// Select all children but `$num`.
+/// @param {Number} $num[] `n` number that should be excluded from all other children
+@mixin all-but($num) {
+ &:not(:nth-child(#{$num})) {
+ @content;
+ }
+}
+
+/// Select all children between the `$num` first and the `$num` last.
+/// @param {Number} $num[] `n` number excluded from first and last from all other children
+@mixin all-but-first-last($num) {
+ &:nth-child(n + #{$num}):nth-last-child(n + #{$num}) {
+ @content;
+ }
+}
+
+/// Will only select the child if it's unique. That means that if there are at least 2 children, the style will not be applied.
+@mixin unique {
+ &:only-child {
+ @content;
+ }
+}
+
+/// Will only select children if they are not unique. That means that if there are at least 2 children, the style will be applied.
+@mixin not-unique() {
+ &:not(:only-child) {
+ @content;
+ }
+}
+
+/// Select all children between `$first` and `$last`.
+/// @param {Number} $first[] First `nth` number
+/// @param {Number} $last[] Last `nth` number
+@mixin between($first, $last) {
+ &:nth-child(n + #{$first}):nth-child(-n + #{$last}) {
+ @content;
+ }
+}
+
+/// Select all even children.
+@mixin even {
+ &:nth-child(even) {
+ @content;
+ }
+}
+
+/// Select all even children between `$first` and `$last`.
+/// @param {Number} $first[] First `nth` number
+/// @param {Number} $last[] Last `nth` number
+@mixin even-between($first, $last) {
+ &:nth-child(even):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
+ @content;
+ }
+}
+
+/// Select all odd children.
+@mixin odd {
+ &:nth-child(odd) {
+ @content;
+ }
+}
+
+/// Select all odd children between `$first` and `$last`.
+/// @param {Number} $first[] First `nth` number
+/// @param {Number} $last[] Last `nth` number
+@mixin odd-between($first, $last) {
+ &:nth-child(odd):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
+ @content;
+ }
+}
+
+/// Select all `$num` children between `$first` and `$last`.
+/// @param {Number} $num[] Every `n` number between `$first` and `$last`.
+/// @param {Number} $first[] First `n` number
+/// @param {Number} $last[] Last `n` number
+@mixin number-between($num, $first, $last) {
+ &:nth-child(#{$num}n):nth-child(n + #{$first}):nth-child(-n + #{$last}) {
+ @content;
+ }
+}
diff --git a/src/foundation/prototype/_rotate.scss b/src/foundation/prototype/_rotate.scss
new file mode 100644
index 0000000..8231db8
--- /dev/null
+++ b/src/foundation/prototype/_rotate.scss
@@ -0,0 +1,31 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-rotate
+////
+
+/// Rotate Mixin: Rotate an element to a certain deg
+/// @param {Number} $deg[] Degree of rotation
+@mixin rotate($deg) {
+ transform:rotate($deg + deg);
+}
+
+/// RotateX Mixin: Rotate an element to a certain deg on X-Axis
+/// @param {Number} $deg[] Degree of rotation
+@mixin rotateX($deg) {
+ transform:rotateX($deg + deg);
+}
+
+/// RotateY Mixin: Rotate an element to a certain deg on Y-Axis
+/// @param {Number} $deg[] Degree of rotation
+@mixin rotateY($deg) {
+ transform:rotateY($deg + deg);
+}
+
+/// RotateZ Mixin: Rotate an element to a certain deg on Z-Axis
+/// @param {Number} $deg[] Degree of rotation
+@mixin rotateZ($deg) {
+ transform:rotateZ($deg + deg);
+} \ No newline at end of file
diff --git a/src/foundation/prototype/_rounded.scss b/src/foundation/prototype/_rounded.scss
new file mode 100644
index 0000000..225fb55
--- /dev/null
+++ b/src/foundation/prototype/_rounded.scss
@@ -0,0 +1,61 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-rounded
+////
+
+/// Responsive breakpoints for rounded utility.
+/// @type Boolean
+$prototype-rounded-breakpoints: $global-prototype-breakpoints !default;
+
+/// Default value for `prototype-border-radius`
+/// @type Number
+$prototype-border-radius: rem-calc(3) !default;
+
+/// Rounded utility (all corners): Adds radius corners (all corners) to an element by default.
+/// @param {Number} $radius [$prototype-border-radius] Border radius (all corners)
+@mixin border-radius(
+ $radius: $prototype-border-radius
+) {
+ border-radius: $radius;
+}
+
+/// Rounded square utility or rectangle utility (all corners): Rounds all corners to an element by default to make a pill shape.
+@mixin border-rounded {
+ border-radius: 5000px !important;
+}
+
+@mixin foundation-prototype-rounded {
+ .rounded {
+ @include border-rounded;
+
+ .switch-paddle {
+ @include border-rounded;
+ &:after {
+ border-radius: 50%; // For switches
+ }
+ }
+ }
+
+ .radius {
+ @include border-radius;
+ }
+
+ @if ($prototype-rounded-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-rounded {
+ @include border-rounded;
+ }
+ .#{$size}-radius {
+ @include border-radius;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_separator.scss b/src/foundation/prototype/_separator.scss
new file mode 100644
index 0000000..9baf121
--- /dev/null
+++ b/src/foundation/prototype/_separator.scss
@@ -0,0 +1,96 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-separator
+////
+
+/// Responsive breakpoints for separator.
+/// @type Boolean
+$prototype-separator-breakpoints: $global-prototype-breakpoints !default;
+
+/// Default alignment of a separator.
+/// @type String
+$prototype-separator-align: center !default;
+
+/// Height of a separator.
+/// @type Number
+$prototype-separator-height: rem-calc(2) !default;
+
+/// Width of a separator.
+/// @type Number
+$prototype-separator-width: 3rem !default;
+
+/// Default color of a separator.
+/// @type Color
+$prototype-separator-background: $primary-color !default;
+
+/// Top Margin of a separator.
+/// @type Number
+$prototype-separator-margin-top: $global-margin !default;
+
+/// Title separator Utility, mostly used to style the main heading of a section
+/// @param {String} $align [$prototype-separator-align] - separator Alignment
+/// @param {Number} $height [$prototype-separator-height] - Width
+/// @param {Number} $width [$prototype-separator-width] - Height
+/// @param {Color} $background [$prototype-separator-background] - Background
+/// @param {Number} $top [$prototype-separator-margin-top] - Margin Top
+@mixin separator (
+ $align: $prototype-separator-align,
+ $height: $prototype-separator-height,
+ $width: $prototype-separator-width,
+ $background: $prototype-separator-background,
+ $top: $prototype-separator-margin-top
+) {
+ text-align: $align !important;
+ @include clearfix;
+
+ &::after {
+ @include position(relative);
+ width: $width;
+ border-bottom: $height solid $background;
+ margin: $top auto 0;
+
+ @if $align == left {
+ margin-left: 0 !important;
+ }
+
+ @if $align == right {
+ margin-right: 0 !important;
+ }
+ }
+}
+
+@mixin foundation-prototype-separator {
+ .separator-center {
+ @include separator(center);
+ }
+
+ .separator-left {
+ @include separator(left);
+ }
+
+ .separator-right {
+ @include separator(right);
+ }
+
+ @if ($prototype-separator-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-separator-center {
+ @include separator(center);
+ }
+ .#{$size}-separator-left {
+ @include separator(left);
+ }
+ .#{$size}-separator-right {
+ @include separator(right);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_shadow.scss b/src/foundation/prototype/_shadow.scss
new file mode 100644
index 0000000..abb44a5
--- /dev/null
+++ b/src/foundation/prototype/_shadow.scss
@@ -0,0 +1,43 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-shadow
+////
+
+/// Responsive breakpoints for shadow utility.
+/// @type Boolean
+$prototype-shadow-breakpoints: $global-prototype-breakpoints !default;
+
+/// Default value for `prototype-box-shadow`
+/// @type Number
+$prototype-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),
+ 0 2px 10px 0 rgba(0,0,0,.12) !default;
+
+/// Shadow Utility: Adds a light box shadow to an element by default.
+/// @param {Number} $shadow [$prototype-box-shadow] Box Shadow of a component
+@mixin shadow(
+ $shadow: $prototype-box-shadow
+) {
+ box-shadow: $shadow;
+}
+
+@mixin foundation-prototype-shadow {
+ .shadow {
+ @include shadow;
+ }
+
+ @if ($prototype-shadow-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-shadow {
+ @include shadow;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_sizing.scss b/src/foundation/prototype/_sizing.scss
new file mode 100644
index 0000000..9b48d48
--- /dev/null
+++ b/src/foundation/prototype/_sizing.scss
@@ -0,0 +1,73 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-sizing
+////
+
+/// Responsive breakpoints for spacing classes (margin and padding)
+/// @type Boolean
+$prototype-sizing-breakpoints: $global-prototype-breakpoints !default;
+
+/// Map containing all the `sizing` classes
+/// @type Map
+$prototype-sizing: (
+ width,
+ height
+) !default;
+
+/// Map containing all the sizes.
+/// @type Map
+$prototype-sizes: (
+ 25: 25%,
+ 50: 50%,
+ 75: 75%,
+ 100: 100%
+) !default;
+
+/// Max Width 100 utility.
+@mixin max-width-100 {
+ max-width: 100% !important;
+}
+
+/// Max Height 100 utility.
+@mixin max-height-100 {
+ max-height: 100% !important;
+}
+
+@mixin foundation-prototype-sizing {
+ // Element Sizing
+ @each $sizing in $prototype-sizing {
+ @each $length, $percentage in $prototype-sizes {
+ .#{$sizing}-#{$length} {
+ #{$sizing}: $percentage !important;
+ }
+ }
+ }
+
+ // Max width & height
+ .max-width-100 {
+ @include max-width-100;
+ }
+ .max-height-100 {
+ @include max-height-100;
+ }
+
+ @if ($prototype-sizing-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ @each $sizing in $prototype-sizing {
+ @each $length, $percentage in $prototype-sizes {
+ .#{$size}-#{$sizing}-#{$length} {
+ #{$sizing}: $percentage !important;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_spacing.scss b/src/foundation/prototype/_spacing.scss
new file mode 100644
index 0000000..3129a2b
--- /dev/null
+++ b/src/foundation/prototype/_spacing.scss
@@ -0,0 +1,179 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-spacing
+////
+
+/// Responsive breakpoints for spacing classes (margin and padding)
+/// @type Boolean
+$prototype-spacing-breakpoints: $global-prototype-breakpoints !default;
+
+/// Default number of spacers count (margin and padding)
+/// @type Number
+$prototype-spacers-count: 3 !default;
+
+/// Margin helper mixin, all the values are multiplied by `$global-margin` which by default is equal to `1rem`
+/// @param {Number} $top [null] - Margin Top
+/// @param {Number} $right [null] - Margin Right
+/// @param {Number} $bottom [null] - Margin Bottom
+/// @param {Number} $left [null] - Margin Left
+@mixin margin(
+ $top: null,
+ $right: null,
+ $bottom: null,
+ $left: null
+) {
+ @if $top != null {
+ margin-top: $top * $global-margin !important;
+ }
+ @if $right != null {
+ margin-right: $right * $global-margin !important;
+ }
+ @if $bottom != null {
+ margin-bottom: $bottom * $global-margin !important;
+ }
+ @if $left != null {
+ margin-left: $left * $global-margin !important;
+ }
+}
+
+/// Padding helper mixin, all the values are multiplied by `$global-padding` which by default is equal to `1rem`
+/// @param {Number} $top [null] - Padding Top
+/// @param {Number} $right [null] - Padding Right
+/// @param {Number} $bottom [null] - Padding Bottom
+/// @param {Number} $left [null] - Padding Left
+@mixin padding(
+ $top: null,
+ $right: null,
+ $bottom: null,
+ $left: null
+) {
+ @if $top != null {
+ padding-top: $top * $global-padding !important;
+ }
+ @if $right != null {
+ padding-right: $right * $global-padding !important;
+ }
+ @if $bottom != null {
+ padding-bottom: $bottom * $global-padding !important;
+ }
+ @if $left != null {
+ padding-left: $left * $global-padding !important;
+ }
+}
+
+/// Margin classes for specific direction properties
+/// @param {String} $dir [] Direction
+/// @param {Number} $spacer [] Spacer
+@mixin margin-direction($dir, $spacer) {
+ @if ($dir == top) {
+ @include margin($top: $spacer);
+ }
+ @else if ($dir == right) {
+ @include margin($right: $spacer);
+ }
+ @else if ($dir == bottom) {
+ @include margin($bottom: $spacer);
+ }
+ @else if ($dir == left) {
+ @include margin($left: $spacer);
+ }
+ @else if ($dir == horizontal) {
+ @include margin($right: $spacer, $left: $spacer);
+ }
+ @else if ($dir == vertical) {
+ @include margin($top: $spacer, $bottom: $spacer);
+ }
+}
+
+/// Padding classes for specific direction properties
+/// @param {String} $dir [] Direction
+/// @param {Number} $spacer [] Spacer
+@mixin padding-direction($dir, $spacer) {
+ @if ($dir == top) {
+ @include padding($top: $spacer);
+ }
+ @else if ($dir == right) {
+ @include padding($right: $spacer);
+ }
+ @else if ($dir == bottom) {
+ @include padding($bottom: $spacer);
+ }
+ @else if ($dir == left) {
+ @include padding($left: $spacer);
+ }
+ @else if ($dir == horizontal) {
+ @include padding($right: $spacer, $left: $spacer);
+ }
+ @else if ($dir == vertical) {
+ @include padding($top: $spacer, $bottom: $spacer);
+ }
+}
+
+@mixin foundation-prototype-spacing {
+ @for $spacer from 0 through $prototype-spacers-count {
+
+ @each $prop in (margin, padding) {
+ // All Sides
+ .#{$prop}-#{$spacer} {
+ @if ($prop == margin) {
+ margin: $spacer * $global-margin !important;
+ }
+ @else if ($prop == padding) {
+ padding: $spacer * $global-padding !important;
+ }
+ }
+
+ @each $dir in (top, right, bottom, left, horizontal, vertical) {
+ // Top Side
+ .#{$prop}-#{$dir}-#{$spacer} {
+ @if ($prop == margin) {
+ @include margin-direction($dir, $spacer);
+ }
+ @else if ($prop == padding) {
+ @include padding-direction($dir, $spacer);
+ }
+ }
+ }
+ }
+ }
+
+ @if ($prototype-spacing-breakpoints) {
+ @for $spacer from 0 through $prototype-spacers-count {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size} {
+ @each $prop in (margin, padding) {
+ // All Sides
+ &-#{$prop}-#{$spacer} {
+ @if ($prop == margin) {
+ margin: $spacer * $global-margin !important;
+ }
+ @else if ($prop == padding) {
+ padding: $spacer * $global-padding !important;
+ }
+ }
+
+ @each $dir in (top, right, bottom, left, horizontal, vertical) {
+ // Top Side
+ &-#{$prop}-#{$dir}-#{$spacer} {
+ @if ($prop == margin) {
+ @include margin-direction($dir, $spacer);
+ }
+ @else if ($prop == padding) {
+ @include padding-direction($dir, $spacer);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_text-decoration.scss b/src/foundation/prototype/_text-decoration.scss
new file mode 100644
index 0000000..8f3c913
--- /dev/null
+++ b/src/foundation/prototype/_text-decoration.scss
@@ -0,0 +1,48 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-text-decoration
+////
+
+/// Responsive breakpoints for text decoration classes
+/// @type Boolean
+$prototype-decoration-breakpoints: $global-prototype-breakpoints !default;
+
+/// Map containing all the `text-decoration` classes
+/// @type Map
+$prototype-text-decoration: (
+ overline,
+ underline,
+ line-through,
+) !default;
+
+/// Text Decoration, by default coming through a map `$prototype-text-decoration`
+/// @param {String} $decoration [] Text Decoration
+@mixin text-decoration($decoration) {
+ text-decoration: $decoration !important;
+}
+
+@mixin foundation-prototype-text-decoration {
+ @each $decoration in $prototype-text-decoration {
+ .text-#{$decoration} {
+ @include text-decoration($decoration);
+ }
+ }
+
+ @if ($prototype-decoration-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @each $decoration in $prototype-text-decoration {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-text-#{$decoration} {
+ @include text-decoration($decoration);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_text-transformation.scss b/src/foundation/prototype/_text-transformation.scss
new file mode 100644
index 0000000..ca9c2e4
--- /dev/null
+++ b/src/foundation/prototype/_text-transformation.scss
@@ -0,0 +1,48 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-text-transformation
+////
+
+/// Responsive breakpoints for text transformation classes
+/// @type Boolean
+$prototype-transformation-breakpoints: $global-prototype-breakpoints !default;
+
+/// Map containing all the `text-transformation` classes
+/// @type Map
+$prototype-text-transformation: (
+ lowercase,
+ uppercase,
+ capitalize
+) !default;
+
+/// Text Transformation, by default coming through a map `$prototype-text-transformation`
+/// @param {String} $transformation [] Text Transformation
+@mixin text-transform($transformation) {
+ text-transform: $transformation !important;
+}
+
+@mixin foundation-prototype-text-transformation {
+ @each $transformation in $prototype-text-transformation {
+ .text-#{$transformation} {
+ @include text-transform($transformation);
+ }
+ }
+
+ @if ($prototype-transformation-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @each $transformation in $prototype-text-transformation {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-text-#{$transformation} {
+ @include text-transform($transformation);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/prototype/_text-utilities.scss b/src/foundation/prototype/_text-utilities.scss
new file mode 100644
index 0000000..8c327a2
--- /dev/null
+++ b/src/foundation/prototype/_text-utilities.scss
@@ -0,0 +1,88 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group prototype-text-utilities
+////
+
+/// Responsive breakpoints for text utilities
+/// @type Boolean
+$prototype-utilities-breakpoints: $global-prototype-breakpoints !default;
+
+/// Default Value for `text-overflow` variable
+/// @type String
+$prototype-text-overflow: ellipsis !default;
+
+/// Image Replacement utility. `text-hide`
+@mixin text-hide {
+ font: 0/0 a !important;
+ color: transparent !important;
+ text-shadow: none !important;
+ background-color: transparent !important;
+ border: 0 !important;
+}
+
+/// Truncating the text, elipsis by default.
+/// @param {String} $overflow [$prototype-text-overflow] Text Truncate
+@mixin text-truncate(
+ $overflow: $prototype-text-overflow
+) {
+ max-width: 100% !important;
+ overflow: hidden !important;
+ text-overflow: $overflow;
+ white-space: nowrap !important;
+}
+
+/// No wrapping of the text. `text-nowrap`
+@mixin text-nowrap {
+ white-space: nowrap !important;
+}
+
+/// Wrapping of the text. `text-wrap`
+@mixin text-wrap {
+ word-wrap: break-word !important;
+}
+
+@mixin foundation-prototype-text-utilities {
+ .text-hide {
+ @include text-hide;
+ }
+
+ .text-truncate {
+ @include text-truncate;
+ }
+
+ .text-nowrap {
+ @include text-nowrap;
+ }
+
+ .text-wrap {
+ @include text-wrap;
+ }
+
+ @if ($prototype-utilities-breakpoints) {
+ // Loop through Responsive Breakpoints
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-text-hide {
+ @include text-hide;
+ }
+
+ .#{$size}-text-truncate {
+ @include text-truncate;
+ }
+
+ .#{$size}-text-nowrap {
+ @include text-nowrap;
+ }
+
+ .#{$size}-text-wrap {
+ @include text-wrap;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/settings/_settings.scss b/src/foundation/settings/_settings.scss
new file mode 100644
index 0000000..8a9ab56
--- /dev/null
+++ b/src/foundation/settings/_settings.scss
@@ -0,0 +1,895 @@
+// Foundation for Sites Settings
+// -----------------------------
+//
+// Table of Contents:
+//
+// 1. Global
+// 2. Breakpoints
+// 3. The Grid
+// 4. Base Typography
+// 5. Typography Helpers
+// 6. Abide
+// 7. Accordion
+// 8. Accordion Menu
+// 9. Badge
+// 10. Breadcrumbs
+// 11. Button
+// 12. Button Group
+// 13. Callout
+// 14. Card
+// 15. Close Button
+// 16. Drilldown
+// 17. Dropdown
+// 18. Dropdown Menu
+// 19. Flexbox Utilities
+// 20. Forms
+// 21. Label
+// 22. Media Object
+// 23. Menu
+// 24. Meter
+// 25. Off-canvas
+// 26. Orbit
+// 27. Pagination
+// 28. Progress Bar
+// 29. Prototype Arrow
+// 30. Prototype Border-Box
+// 31. Prototype Border-None
+// 32. Prototype Bordered
+// 33. Prototype Display
+// 34. Prototype Font-Styling
+// 35. Prototype List-Style-Type
+// 36. Prototype Overflow
+// 37. Prototype Position
+// 38. Prototype Rounded
+// 39. Prototype Separator
+// 40. Prototype Shadow
+// 41. Prototype Sizing
+// 42. Prototype Spacing
+// 43. Prototype Text-Decoration
+// 44. Prototype Text-Transformation
+// 45. Prototype Text-Utilities
+// 46. Responsive Embed
+// 47. Reveal
+// 48. Slider
+// 49. Switch
+// 50. Table
+// 51. Tabs
+// 52. Thumbnail
+// 53. Title Bar
+// 54. Tooltip
+// 55. Top Bar
+// 56. Xy Grid
+
+@import 'util/util';
+
+// 1. Global
+// ---------
+
+$global-font-size: 100%;
+$global-width: rem-calc(1200);
+$global-lineheight: 1.5;
+$foundation-palette: (
+ primary: #1779ba,
+ secondary: #767676,
+ success: #3adb76,
+ warning: #ffae00,
+ alert: #cc4b37,
+);
+$light-gray: #e6e6e6;
+$medium-gray: #cacaca;
+$dark-gray: #8a8a8a;
+$black: #0a0a0a;
+$white: #fefefe;
+$body-background: $white;
+$body-font-color: $black;
+$body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;
+$body-antialiased: true;
+$global-margin: 1rem;
+$global-padding: 1rem;
+$global-position: 1rem;
+$global-weight-normal: normal;
+$global-weight-bold: bold;
+$global-radius: 0;
+$global-menu-padding: 0.7rem 1rem;
+$global-menu-nested-margin: 1rem;
+$global-text-direction: ltr;
+$global-flexbox: true;
+$global-prototype-breakpoints: false;
+$global-button-cursor: auto;
+$global-color-pick-contrast-tolerance: 0;
+$print-transparent-backgrounds: true;
+$print-hrefs: true;
+
+@include add-foundation-colors;
+
+// 2. Breakpoints
+// --------------
+
+$breakpoints: (
+ small: 0,
+ medium: 640px,
+ large: 1024px,
+ xlarge: 1200px,
+ xxlarge: 1440px,
+);
+$breakpoints-hidpi: (
+ hidpi-1: 1,
+ hidpi-1-5: 1.5,
+ hidpi-2: 2,
+ retina: 2,
+ hidpi-3: 3
+);
+$print-breakpoint: large;
+$breakpoint-classes: (small medium large);
+
+// 3. The Grid
+// -----------
+
+$grid-row-width: $global-width;
+$grid-column-count: 12;
+$grid-column-gutter: (
+ small: 20px,
+ medium: 30px,
+);
+$grid-column-align-edge: true;
+$grid-column-alias: 'columns';
+$block-grid-max: 8;
+
+// 4. Base Typography
+// ------------------
+
+$header-font-family: $body-font-family;
+$header-font-weight: $global-weight-normal;
+$header-font-style: normal;
+$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace;
+$header-color: inherit;
+$header-lineheight: 1.4;
+$header-margin-bottom: 0.5rem;
+$header-styles: (
+ small: (
+ 'h1': ('font-size': 24),
+ 'h2': ('font-size': 20),
+ 'h3': ('font-size': 19),
+ 'h4': ('font-size': 18),
+ 'h5': ('font-size': 17),
+ 'h6': ('font-size': 16),
+ ),
+ medium: (
+ 'h1': ('font-size': 48),
+ 'h2': ('font-size': 40),
+ 'h3': ('font-size': 31),
+ 'h4': ('font-size': 25),
+ 'h5': ('font-size': 20),
+ 'h6': ('font-size': 16),
+ ),
+);
+$header-text-rendering: optimizeLegibility;
+$small-font-size: 80%;
+$header-small-font-color: $medium-gray;
+$paragraph-lineheight: 1.6;
+$paragraph-margin-bottom: 1rem;
+$paragraph-text-rendering: optimizeLegibility;
+$enable-code-inline: true;
+$anchor-color: $primary-color;
+$anchor-color-hover: scale-color($anchor-color, $lightness: -14%);
+$anchor-text-decoration: none;
+$anchor-text-decoration-hover: none;
+$hr-width: $global-width;
+$hr-border: 1px solid $medium-gray;
+$hr-margin: rem-calc(20) auto;
+$list-lineheight: $paragraph-lineheight;
+$list-margin-bottom: $paragraph-margin-bottom;
+$list-style-type: disc;
+$list-style-position: outside;
+$list-side-margin: 1.25rem;
+$list-nested-side-margin: 1.25rem;
+$defnlist-margin-bottom: 1rem;
+$defnlist-term-weight: $global-weight-bold;
+$defnlist-term-margin-bottom: 0.3rem;
+$blockquote-color: $dark-gray;
+$blockquote-padding: rem-calc(9 20 0 19);
+$blockquote-border: 1px solid $medium-gray;
+$enable-cite-block: true;
+$keystroke-font: $font-family-monospace;
+$keystroke-color: $black;
+$keystroke-background: $light-gray;
+$keystroke-padding: rem-calc(2 4 0);
+$keystroke-radius: $global-radius;
+$abbr-underline: 1px dotted $black;
+
+// 5. Typography Helpers
+// ---------------------
+
+$lead-font-size: $global-font-size * 1.25;
+$lead-lineheight: 1.6;
+$subheader-lineheight: 1.4;
+$subheader-color: $dark-gray;
+$subheader-font-weight: $global-weight-normal;
+$subheader-margin-top: 0.2rem;
+$subheader-margin-bottom: 0.5rem;
+$stat-font-size: 2.5rem;
+$cite-color: $dark-gray;
+$cite-font-size: rem-calc(13);
+$cite-pseudo-content: '\2014 \0020';
+$code-color: $black;
+$code-font-family: $font-family-monospace;
+$code-font-weight: $global-weight-normal;
+$code-background: $light-gray;
+$code-border: 1px solid $medium-gray;
+$code-padding: rem-calc(2 5 1);
+$code-block-padding: 1rem;
+$code-block-margin-bottom: 1.5rem;
+
+// 6. Abide
+// --------
+
+$abide-inputs: true;
+$abide-labels: true;
+$input-background-invalid: get-color(alert);
+$form-label-color-invalid: get-color(alert);
+$input-error-color: get-color(alert);
+$input-error-font-size: rem-calc(12);
+$input-error-font-weight: $global-weight-bold;
+
+// 7. Accordion
+// ------------
+
+$accordion-background: $white;
+$accordion-plusminus: true;
+$accordion-plus-content: '\002B';
+$accordion-minus-content: '\2013';
+$accordion-title-font-size: rem-calc(12);
+$accordion-item-color: $primary-color;
+$accordion-item-background-hover: $light-gray;
+$accordion-item-padding: 1.25rem 1rem;
+$accordion-content-background: $white;
+$accordion-content-border: 1px solid $light-gray;
+$accordion-content-color: $body-font-color;
+$accordion-content-padding: 1rem;
+
+// 8. Accordion Menu
+// -----------------
+
+$accordionmenu-padding: $global-menu-padding;
+$accordionmenu-nested-margin: $global-menu-nested-margin;
+$accordionmenu-submenu-padding: $accordionmenu-padding;
+$accordionmenu-arrows: true;
+$accordionmenu-arrow-color: $primary-color;
+$accordionmenu-item-background: null;
+$accordionmenu-border: null;
+$accordionmenu-submenu-toggle-background: null;
+$accordion-submenu-toggle-border: $accordionmenu-border;
+$accordionmenu-submenu-toggle-width: 40px;
+$accordionmenu-submenu-toggle-height: $accordionmenu-submenu-toggle-width;
+$accordionmenu-arrow-size: 6px;
+
+// 9. Badge
+// --------
+
+$badge-background: $primary-color;
+$badge-color: $white;
+$badge-color-alt: $black;
+$badge-palette: $foundation-palette;
+$badge-padding: 0.3em;
+$badge-minwidth: 2.1em;
+$badge-font-size: 0.6rem;
+
+// 10. Breadcrumbs
+// ---------------
+
+$breadcrumbs-margin: 0 0 $global-margin 0;
+$breadcrumbs-item-font-size: rem-calc(11);
+$breadcrumbs-item-color: $primary-color;
+$breadcrumbs-item-color-current: $black;
+$breadcrumbs-item-color-disabled: $medium-gray;
+$breadcrumbs-item-margin: 0.75rem;
+$breadcrumbs-item-uppercase: true;
+$breadcrumbs-item-separator: true;
+$breadcrumbs-item-separator-item: '/';
+$breadcrumbs-item-separator-item-rtl: '\\';
+$breadcrumbs-item-separator-color: $medium-gray;
+
+// 11. Button
+// ----------
+
+$button-font-family: inherit;
+$button-font-weight: null;
+$button-padding: 0.85em 1em;
+$button-margin: 0 0 $global-margin 0;
+$button-fill: solid;
+$button-background: $primary-color;
+$button-background-hover: scale-color($button-background, $lightness: -15%);
+$button-color: $white;
+$button-color-alt: $black;
+$button-radius: $global-radius;
+$button-border: 1px solid transparent;
+$button-hollow-border-width: 1px;
+$button-sizes: (
+ tiny: 0.6rem,
+ small: 0.75rem,
+ default: 0.9rem,
+ large: 1.25rem,
+);
+$button-palette: $foundation-palette;
+$button-opacity-disabled: 0.25;
+$button-background-hover-lightness: -20%;
+$button-hollow-hover-lightness: -50%;
+$button-transition: background-color 0.25s ease-out, color 0.25s ease-out;
+$button-responsive-expanded: false;
+
+// 12. Button Group
+// ----------------
+
+$buttongroup-margin: 1rem;
+$buttongroup-spacing: 1px;
+$buttongroup-child-selector: '.button';
+$buttongroup-expand-max: 6;
+$buttongroup-radius-on-each: true;
+
+// 13. Callout
+// -----------
+
+$callout-background: $white;
+$callout-background-fade: 85%;
+$callout-border: 1px solid rgba($black, 0.25);
+$callout-margin: 0 0 1rem 0;
+$callout-sizes: (
+ small: 0.5rem,
+ default: 1rem,
+ large: 3rem,
+);
+$callout-font-color: $body-font-color;
+$callout-font-color-alt: $body-background;
+$callout-radius: $global-radius;
+$callout-link-tint: 30%;
+
+// 14. Card
+// --------
+
+$card-background: $white;
+$card-font-color: $body-font-color;
+$card-divider-background: $light-gray;
+$card-border: 1px solid $light-gray;
+$card-shadow: none;
+$card-border-radius: $global-radius;
+$card-padding: $global-padding;
+$card-margin-bottom: $global-margin;
+
+// 15. Close Button
+// ----------------
+
+$closebutton-position: right top;
+$closebutton-z-index: 10;
+$closebutton-default-size: medium;
+$closebutton-offset-horizontal: (
+ small: 0.66rem,
+ medium: 1rem,
+);
+$closebutton-offset-vertical: (
+ small: 0.33em,
+ medium: 0.5rem,
+);
+$closebutton-size: (
+ small: 1.5em,
+ medium: 2em,
+);
+$closebutton-lineheight: 1;
+$closebutton-color: $dark-gray;
+$closebutton-color-hover: $black;
+
+// 16. Drilldown
+// -------------
+
+$drilldown-transition: transform 0.15s linear;
+$drilldown-arrows: true;
+$drilldown-padding: $global-menu-padding;
+$drilldown-nested-margin: 0;
+$drilldown-background: $white;
+$drilldown-submenu-padding: $drilldown-padding;
+$drilldown-submenu-background: $white;
+$drilldown-arrow-color: $primary-color;
+$drilldown-arrow-size: 6px;
+
+// 17. Dropdown
+// ------------
+
+$dropdown-padding: 1rem;
+$dropdown-background: $body-background;
+$dropdown-border: 1px solid $medium-gray;
+$dropdown-font-size: 1rem;
+$dropdown-width: 300px;
+$dropdown-radius: $global-radius;
+$dropdown-sizes: (
+ tiny: 100px,
+ small: 200px,
+ large: 400px,
+);
+
+// 18. Dropdown Menu
+// -----------------
+
+$dropdownmenu-arrows: true;
+$dropdownmenu-arrow-color: $anchor-color;
+$dropdownmenu-arrow-size: 6px;
+$dropdownmenu-arrow-padding: 1.5rem;
+$dropdownmenu-min-width: 200px;
+$dropdownmenu-background: null;
+$dropdownmenu-submenu-background: $white;
+$dropdownmenu-padding: $global-menu-padding;
+$dropdownmenu-nested-margin: 0;
+$dropdownmenu-submenu-padding: $dropdownmenu-padding;
+$dropdownmenu-border: 1px solid $medium-gray;
+$dropdown-menu-item-color-active: get-color(primary);
+$dropdown-menu-item-background-active: transparent;
+
+// 19. Flexbox Utilities
+// ---------------------
+
+$flex-source-ordering-count: 6;
+$flexbox-responsive-breakpoints: true;
+
+// 20. Forms
+// ---------
+
+$fieldset-border: 1px solid $medium-gray;
+$fieldset-padding: rem-calc(20);
+$fieldset-margin: rem-calc(18 0);
+$legend-padding: rem-calc(0 3);
+$form-spacing: rem-calc(16);
+$helptext-color: $black;
+$helptext-font-size: rem-calc(13);
+$helptext-font-style: italic;
+$input-prefix-color: $black;
+$input-prefix-background: $light-gray;
+$input-prefix-border: 1px solid $medium-gray;
+$input-prefix-padding: 1rem;
+$form-label-color: $black;
+$form-label-font-size: rem-calc(14);
+$form-label-font-weight: $global-weight-normal;
+$form-label-line-height: 1.8;
+$select-background: $white;
+$select-triangle-color: $dark-gray;
+$select-radius: $global-radius;
+$input-color: $black;
+$input-placeholder-color: $medium-gray;
+$input-font-family: inherit;
+$input-font-size: rem-calc(16);
+$input-font-weight: $global-weight-normal;
+$input-line-height: $global-lineheight;
+$input-background: $white;
+$input-background-focus: $white;
+$input-background-disabled: $light-gray;
+$input-border: 1px solid $medium-gray;
+$input-border-focus: 1px solid $dark-gray;
+$input-padding: $form-spacing / 2;
+$input-shadow: inset 0 1px 2px rgba($black, 0.1);
+$input-shadow-focus: 0 0 5px $medium-gray;
+$input-cursor-disabled: not-allowed;
+$input-transition: box-shadow 0.5s, border-color 0.25s ease-in-out;
+$input-number-spinners: true;
+$input-radius: $global-radius;
+$form-button-radius: $global-radius;
+
+// 21. Label
+// ---------
+
+$label-background: $primary-color;
+$label-color: $white;
+$label-color-alt: $black;
+$label-palette: $foundation-palette;
+$label-font-size: 0.8rem;
+$label-padding: 0.33333rem 0.5rem;
+$label-radius: $global-radius;
+
+// 22. Media Object
+// ----------------
+
+$mediaobject-margin-bottom: $global-margin;
+$mediaobject-section-padding: $global-padding;
+$mediaobject-image-width-stacked: 100%;
+
+// 23. Menu
+// --------
+
+$menu-margin: 0;
+$menu-nested-margin: $global-menu-nested-margin;
+$menu-items-padding: $global-menu-padding;
+$menu-simple-margin: 1rem;
+$menu-item-color-active: $white;
+$menu-item-color-alt-active: $black;
+$menu-item-background-active: get-color(primary);
+$menu-icon-spacing: 0.25rem;
+$menu-state-back-compat: true;
+$menu-centered-back-compat: true;
+$menu-icons-back-compat: true;
+
+// 24. Meter
+// ---------
+
+$meter-height: 1rem;
+$meter-radius: $global-radius;
+$meter-background: $medium-gray;
+$meter-fill-good: $success-color;
+$meter-fill-medium: $warning-color;
+$meter-fill-bad: $alert-color;
+
+// 25. Off-canvas
+// --------------
+
+$offcanvas-sizes: (
+ small: 250px,
+);
+$offcanvas-vertical-sizes: (
+ small: 250px,
+);
+$offcanvas-background: $light-gray;
+$offcanvas-shadow: 0 0 10px rgba($black, 0.7);
+$offcanvas-inner-shadow-size: 20px;
+$offcanvas-inner-shadow-color: rgba($black, 0.25);
+$offcanvas-overlay-zindex: 11;
+$offcanvas-push-zindex: 12;
+$offcanvas-overlap-zindex: 13;
+$offcanvas-reveal-zindex: 12;
+$offcanvas-transition-length: 0.5s;
+$offcanvas-transition-timing: ease;
+$offcanvas-fixed-reveal: true;
+$offcanvas-exit-background: rgba($white, 0.25);
+$maincontent-class: 'off-canvas-content';
+
+// 26. Orbit
+// ---------
+
+$orbit-bullet-background: $medium-gray;
+$orbit-bullet-background-active: $dark-gray;
+$orbit-bullet-diameter: 1.2rem;
+$orbit-bullet-margin: 0.1rem;
+$orbit-bullet-margin-top: 0.8rem;
+$orbit-bullet-margin-bottom: 0.8rem;
+$orbit-caption-background: rgba($black, 0.5);
+$orbit-caption-padding: 1rem;
+$orbit-control-background-hover: rgba($black, 0.5);
+$orbit-control-padding: 1rem;
+$orbit-control-zindex: 10;
+
+// 27. Pagination
+// --------------
+
+$pagination-font-size: rem-calc(14);
+$pagination-margin-bottom: $global-margin;
+$pagination-item-color: $black;
+$pagination-item-padding: rem-calc(3 10);
+$pagination-item-spacing: rem-calc(1);
+$pagination-radius: $global-radius;
+$pagination-item-background-hover: $light-gray;
+$pagination-item-background-current: $primary-color;
+$pagination-item-color-current: $white;
+$pagination-item-color-disabled: $medium-gray;
+$pagination-ellipsis-color: $black;
+$pagination-mobile-items: false;
+$pagination-mobile-current-item: false;
+$pagination-arrows: true;
+$pagination-arrow-previous: '\00AB';
+$pagination-arrow-next: '\00BB';
+
+// 28. Progress Bar
+// ----------------
+
+$progress-height: 1rem;
+$progress-background: $medium-gray;
+$progress-margin-bottom: $global-margin;
+$progress-meter-background: $primary-color;
+$progress-radius: $global-radius;
+
+// 29. Prototype Arrow
+// -------------------
+
+$prototype-arrow-directions: (
+ down,
+ up,
+ right,
+ left
+);
+$prototype-arrow-size: 0.4375rem;
+$prototype-arrow-color: $black;
+
+// 30. Prototype Border-Box
+// ------------------------
+
+$prototype-border-box-breakpoints: $global-prototype-breakpoints;
+
+// 31. Prototype Border-None
+// -------------------------
+
+$prototype-border-none-breakpoints: $global-prototype-breakpoints;
+
+// 32. Prototype Bordered
+// ----------------------
+
+$prototype-bordered-breakpoints: $global-prototype-breakpoints;
+$prototype-border-width: rem-calc(1);
+$prototype-border-type: solid;
+$prototype-border-color: $medium-gray;
+
+// 33. Prototype Display
+// ---------------------
+
+$prototype-display-breakpoints: $global-prototype-breakpoints;
+$prototype-display: (
+ inline,
+ inline-block,
+ block,
+ table,
+ table-cell
+);
+
+// 34. Prototype Font-Styling
+// --------------------------
+
+$prototype-font-breakpoints: $global-prototype-breakpoints;
+$prototype-wide-letter-spacing: rem-calc(4);
+$prototype-font-normal: $global-weight-normal;
+$prototype-font-bold: $global-weight-bold;
+
+// 35. Prototype List-Style-Type
+// -----------------------------
+
+$prototype-list-breakpoints: $global-prototype-breakpoints;
+$prototype-style-type-unordered: (
+ disc,
+ circle,
+ square
+);
+$prototype-style-type-ordered: (
+ decimal,
+ lower-alpha,
+ lower-latin,
+ lower-roman,
+ upper-alpha,
+ upper-latin,
+ upper-roman
+);
+
+// 36. Prototype Overflow
+// ----------------------
+
+$prototype-overflow-breakpoints: $global-prototype-breakpoints;
+$prototype-overflow: (
+ visible,
+ hidden,
+ scroll
+);
+
+// 37. Prototype Position
+// ----------------------
+
+$prototype-position-breakpoints: $global-prototype-breakpoints;
+$prototype-position: (
+ static,
+ relative,
+ absolute,
+ fixed
+);
+$prototype-position-z-index: 975;
+
+// 38. Prototype Rounded
+// ---------------------
+
+$prototype-rounded-breakpoints: $global-prototype-breakpoints;
+$prototype-border-radius: rem-calc(3);
+
+// 39. Prototype Separator
+// -----------------------
+
+$prototype-separator-breakpoints: $global-prototype-breakpoints;
+$prototype-separator-align: center;
+$prototype-separator-height: rem-calc(2);
+$prototype-separator-width: 3rem;
+$prototype-separator-background: $primary-color;
+$prototype-separator-margin-top: $global-margin;
+
+// 40. Prototype Shadow
+// --------------------
+
+$prototype-shadow-breakpoints: $global-prototype-breakpoints;
+$prototype-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),
+ 0 2px 10px 0 rgba(0,0,0,.12);
+
+// 41. Prototype Sizing
+// --------------------
+
+$prototype-sizing-breakpoints: $global-prototype-breakpoints;
+$prototype-sizing: (
+ width,
+ height
+);
+$prototype-sizes: (
+ 25: 25%,
+ 50: 50%,
+ 75: 75%,
+ 100: 100%
+);
+
+// 42. Prototype Spacing
+// ---------------------
+
+$prototype-spacing-breakpoints: $global-prototype-breakpoints;
+$prototype-spacers-count: 3;
+
+// 43. Prototype Text-Decoration
+// -----------------------------
+
+$prototype-decoration-breakpoints: $global-prototype-breakpoints;
+$prototype-text-decoration: (
+ overline,
+ underline,
+ line-through,
+);
+
+// 44. Prototype Text-Transformation
+// ---------------------------------
+
+$prototype-transformation-breakpoints: $global-prototype-breakpoints;
+$prototype-text-transformation: (
+ lowercase,
+ uppercase,
+ capitalize
+);
+
+// 45. Prototype Text-Utilities
+// ----------------------------
+
+$prototype-utilities-breakpoints: $global-prototype-breakpoints;
+$prototype-text-overflow: ellipsis;
+
+// 46. Responsive Embed
+// --------------------
+
+$responsive-embed-margin-bottom: rem-calc(16);
+$responsive-embed-ratios: (
+ default: 4 by 3,
+ widescreen: 16 by 9,
+);
+
+// 47. Reveal
+// ----------
+
+$reveal-background: $white;
+$reveal-width: 600px;
+$reveal-max-width: $global-width;
+$reveal-padding: $global-padding;
+$reveal-border: 1px solid $medium-gray;
+$reveal-radius: $global-radius;
+$reveal-zindex: 1005;
+$reveal-overlay-background: rgba($black, 0.45);
+
+// 48. Slider
+// ----------
+
+$slider-width-vertical: 0.5rem;
+$slider-transition: all 0.2s ease-in-out;
+$slider-height: 0.5rem;
+$slider-background: $light-gray;
+$slider-fill-background: $medium-gray;
+$slider-handle-height: 1.4rem;
+$slider-handle-width: 1.4rem;
+$slider-handle-background: $primary-color;
+$slider-opacity-disabled: 0.25;
+$slider-radius: $global-radius;
+
+// 49. Switch
+// ----------
+
+$switch-background: $medium-gray;
+$switch-background-active: $primary-color;
+$switch-height: 2rem;
+$switch-height-tiny: 1.5rem;
+$switch-height-small: 1.75rem;
+$switch-height-large: 2.5rem;
+$switch-radius: $global-radius;
+$switch-margin: $global-margin;
+$switch-paddle-background: $white;
+$switch-paddle-offset: 0.25rem;
+$switch-paddle-radius: $global-radius;
+$switch-paddle-transition: all 0.25s ease-out;
+$switch-opacity-disabled: .5;
+$switch-cursor-disabled: not-allowed;
+
+// 50. Table
+// ---------
+
+$table-background: $white;
+$table-color-scale: 5%;
+$table-border: 1px solid smart-scale($table-background, $table-color-scale);
+$table-padding: rem-calc(8 10 10);
+$table-hover-scale: 2%;
+$table-row-hover: darken($table-background, $table-hover-scale);
+$table-row-stripe-hover: darken($table-background, $table-color-scale + $table-hover-scale);
+$table-is-striped: true;
+$table-striped-background: smart-scale($table-background, $table-color-scale);
+$table-stripe: even;
+$table-head-background: smart-scale($table-background, $table-color-scale / 2);
+$table-head-row-hover: darken($table-head-background, $table-hover-scale);
+$table-foot-background: smart-scale($table-background, $table-color-scale);
+$table-foot-row-hover: darken($table-foot-background, $table-hover-scale);
+$table-head-font-color: $body-font-color;
+$table-foot-font-color: $body-font-color;
+$show-header-for-stacked: false;
+$table-stack-breakpoint: medium;
+
+// 51. Tabs
+// --------
+
+$tab-margin: 0;
+$tab-background: $white;
+$tab-color: $primary-color;
+$tab-background-active: $light-gray;
+$tab-active-color: $primary-color;
+$tab-item-font-size: rem-calc(12);
+$tab-item-background-hover: $white;
+$tab-item-padding: 1.25rem 1.5rem;
+$tab-content-background: $white;
+$tab-content-border: $light-gray;
+$tab-content-color: $body-font-color;
+$tab-content-padding: 1rem;
+
+// 52. Thumbnail
+// -------------
+
+$thumbnail-border: 4px solid $white;
+$thumbnail-margin-bottom: $global-margin;
+$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2);
+$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5);
+$thumbnail-transition: box-shadow 200ms ease-out;
+$thumbnail-radius: $global-radius;
+
+// 53. Title Bar
+// -------------
+
+$titlebar-background: $black;
+$titlebar-color: $white;
+$titlebar-padding: 0.5rem;
+$titlebar-text-font-weight: bold;
+$titlebar-icon-color: $white;
+$titlebar-icon-color-hover: $medium-gray;
+$titlebar-icon-spacing: 0.25rem;
+
+// 54. Tooltip
+// -----------
+
+$has-tip-cursor: help;
+$has-tip-font-weight: $global-weight-bold;
+$has-tip-border-bottom: dotted 1px $dark-gray;
+$tooltip-background-color: $black;
+$tooltip-color: $white;
+$tooltip-padding: 0.75rem;
+$tooltip-max-width: 10rem;
+$tooltip-font-size: $small-font-size;
+$tooltip-pip-width: 0.75rem;
+$tooltip-pip-height: $tooltip-pip-width * 0.866;
+$tooltip-radius: $global-radius;
+
+// 55. Top Bar
+// -----------
+
+$topbar-padding: 0.5rem;
+$topbar-background: $light-gray;
+$topbar-submenu-background: $topbar-background;
+$topbar-title-spacing: 0.5rem 1rem 0.5rem 0;
+$topbar-input-width: 200px;
+$topbar-unstack-breakpoint: medium;
+
+// 56. Xy Grid
+// -----------
+
+$xy-grid: true;
+$grid-container: $global-width;
+$grid-columns: 12;
+$grid-margin-gutters: (
+ small: 20px,
+ medium: 30px
+);
+$grid-padding-gutters: $grid-margin-gutters;
+$grid-container-padding: $grid-padding-gutters;
+$grid-container-max: $global-width;
+$xy-block-grid-max: 8;
+
diff --git a/src/foundation/typography/_alignment.scss b/src/foundation/typography/_alignment.scss
new file mode 100644
index 0000000..77ffd61
--- /dev/null
+++ b/src/foundation/typography/_alignment.scss
@@ -0,0 +1,22 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+@mixin foundation-text-alignment {
+ @each $size in $breakpoint-classes {
+ @include breakpoint($size) {
+ @each $align in (left, right, center, justify) {
+ @if $size != $-zf-zero-breakpoint {
+ .#{$size}-text-#{$align} {
+ text-align: $align;
+ }
+ }
+ @else {
+ .text-#{$align} {
+ text-align: $align;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/typography/_base.scss b/src/foundation/typography/_base.scss
new file mode 100644
index 0000000..8ea58c9
--- /dev/null
+++ b/src/foundation/typography/_base.scss
@@ -0,0 +1,474 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group typography-base
+////
+
+// Base Typography
+// - - - - - - - - - - - - - - -
+// These are styles applied to basic HTML tags, including:
+// - Paragraphs <p>
+// - Bold/italics <b> <strong> <i> <em>
+// - Small text <small>
+// - Headings <h1>-<h6>
+// - Anchors <a>
+// - Dividers <hr>
+// - Lists <ul> <ol> <dl>
+// - Blockquotes <blockquote>
+// - Code blocks <code>
+// - Abbreviations <abbr>
+// - Citations <cite>
+// - Keystrokes <kbd>
+
+/// Font family for header elements.
+/// @type String | List
+$header-font-family: $body-font-family !default;
+
+/// Font weight of headers.
+/// @type String
+$header-font-weight: $global-weight-normal !default;
+
+/// Font style (e.g. italicized) of headers.
+/// @type String
+$header-font-style: normal !default;
+
+/// Font stack used for elements that use monospaced type, such as code samples
+/// @type String | List
+$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace !default;
+
+/// Color of headers.
+/// @type Color
+$header-color: inherit !default;
+
+/// Line height of headers.
+/// @type Number
+$header-lineheight: 1.4 !default;
+
+/// Bottom margin of headers.
+/// @type Number
+$header-margin-bottom: 0.5rem !default;
+
+/// Styles for headings at various screen sizes. Each key is a breakpoint, and each value is a map of heading styles.
+/// @type Map
+$header-styles: (
+ small: (
+ 'h1': ('font-size': 24),
+ 'h2': ('font-size': 20),
+ 'h3': ('font-size': 19),
+ 'h4': ('font-size': 18),
+ 'h5': ('font-size': 17),
+ 'h6': ('font-size': 16),
+ ),
+ medium: (
+ 'h1': ('font-size': 48),
+ 'h2': ('font-size': 40),
+ 'h3': ('font-size': 31),
+ 'h4': ('font-size': 25),
+ 'h5': ('font-size': 20),
+ 'h6': ('font-size': 16),
+ ),
+) !default;
+
+// $header-styles map is built from $header-sizes in order to ensure downward compatibility
+// when $header-sizes is depreciated, $header-styles needs to get !default values like settings.scss
+@function build_from_header-sizes($header-sizes) {
+ @warn 'Note, that $header-sizes has been replaced with $header-styles. $header-sizes still works, but it is going to be depreciated.';
+ $header-styles: ();
+ @each $size, $headers in $header-sizes {
+ $header-map: ();
+ @each $header, $font-size in $headers {
+ $header-map: map-merge($header-map, ($header: ('font-size': $font-size)));
+ }
+ $header-styles: map-merge($header-styles, ($size: $header-map));
+ }
+ @return $header-styles;
+}
+
+// If it exists $headers-sizes is used to build $header-styles. See the documentation.
+@if variable-exists(header-sizes) {
+ $header-styles: build_from_header-sizes($header-sizes);
+}
+
+/// Text rendering method of headers.
+/// @type String
+$header-text-rendering: optimizeLegibility !default;
+
+/// Font size of `<small>` elements.
+/// @type Number
+$small-font-size: 80% !default;
+
+/// Color of `<small>` elements when placed inside headers.
+/// @type Color
+$header-small-font-color: $medium-gray !default;
+
+/// Line height of text inside `<p>` elements.
+/// @type Number
+$paragraph-lineheight: 1.6 !default;
+
+/// Bottom margin of paragraphs.
+/// @type Number
+$paragraph-margin-bottom: 1rem !default;
+
+/// Text rendering method for paragraph text.
+/// @type String
+$paragraph-text-rendering: optimizeLegibility !default;
+
+/// Use the `.code-inline` component as default for `<code>` elements.
+/// @type Boolean
+$enable-code-inline: true;
+
+/// Default color for links.
+/// @type Color
+$anchor-color: $primary-color !default;
+
+/// Default color for links on hover.
+/// @type Color
+$anchor-color-hover: scale-color($anchor-color, $lightness: -14%) !default;
+
+/// Default text decoration for links.
+/// @type String
+$anchor-text-decoration: none !default;
+
+/// Default text decoration for links on hover.
+/// @type String
+$anchor-text-decoration-hover: none !default;
+
+/// Maximum width of a divider.
+/// @type Number
+$hr-width: $global-width !default;
+
+/// Default border for a divider.
+/// @type List
+$hr-border: 1px solid $medium-gray !default;
+
+/// Default margin for a divider.
+/// @type Number | List
+$hr-margin: rem-calc(20) auto !default;
+
+/// Line height for items in a list.
+/// @type Number
+$list-lineheight: $paragraph-lineheight !default;
+
+/// Bottom margin for items in a list.
+/// @type Number
+$list-margin-bottom: $paragraph-margin-bottom !default;
+
+/// Bullet type to use for unordered lists (e.g., `square`, `circle`, `disc`).
+/// @type String
+$list-style-type: disc !default;
+
+/// Positioning for bullets on unordered list items.
+/// @type String
+$list-style-position: outside !default;
+
+/// Left (or right) margin for lists.
+/// @type Number
+$list-side-margin: 1.25rem !default;
+
+/// Left (or right) margin for a list inside a list.
+/// @type Number
+$list-nested-side-margin: 1.25rem !default;
+
+/// Bottom margin for `<dl>` elements.
+/// @type Number
+$defnlist-margin-bottom: 1rem !default;
+
+/// Font weight for `<dt>` elements.
+/// @type String
+$defnlist-term-weight: $global-weight-bold !default;
+
+/// Spacing between `<dt>` and `<dd>` elements.
+/// @type Number
+$defnlist-term-margin-bottom: 0.3rem !default;
+
+/// Text color of `<blockquote>` elements.
+/// @type Color
+$blockquote-color: $dark-gray !default;
+
+/// Padding inside a `<blockquote>` element.
+/// @type Number | List
+$blockquote-padding: rem-calc(9 20 0 19) !default;
+
+/// Side border for `<blockquote>` elements.
+/// @type List
+$blockquote-border: 1px solid $medium-gray !default;
+
+/// Use the `.cite-block` component as default for `<cite>` elements.
+/// @type Boolean
+$enable-cite-block: true;
+
+/// Font family for `<kbd>` elements.
+/// @type String | List
+$keystroke-font: $font-family-monospace !default;
+
+/// Text color for `<kbd>` elements.
+/// @type Color
+$keystroke-color: $black !default;
+
+/// Background color for `<kbd>` elements.
+/// @type Color
+$keystroke-background: $light-gray !default;
+
+/// Padding for `<kbd>` elements.
+/// @type Number | List
+$keystroke-padding: rem-calc(2 4 0) !default;
+
+/// Border radius for `<kbd>` elements.
+/// @type Number | List
+$keystroke-radius: $global-radius !default;
+
+/// Bottom border style for `<abbr>` elements.
+/// @type List
+$abbr-underline: 1px dotted $black !default;
+
+@mixin foundation-typography-base {
+ // Typography resets
+ div,
+ dl,
+ dt,
+ dd,
+ ul,
+ ol,
+ li,
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ pre,
+ form,
+ p,
+ blockquote,
+ th,
+ td {
+ margin: 0;
+ padding: 0;
+ }
+
+ // Paragraphs
+ p {
+ margin-bottom: $paragraph-margin-bottom;
+
+ font-size: inherit;
+ line-height: $paragraph-lineheight;
+ text-rendering: $paragraph-text-rendering;
+ }
+
+ // Emphasized text
+ em,
+ i {
+ font-style: italic;
+ line-height: inherit;
+ }
+
+ // Strong text
+ strong,
+ b {
+ font-weight: $global-weight-bold;
+ line-height: inherit;
+ }
+
+ // Small text
+ small {
+ font-size: $small-font-size;
+ line-height: inherit;
+ }
+
+ // Headings
+ h1, .h1,
+ h2, .h2,
+ h3, .h3,
+ h4, .h4,
+ h5, .h5,
+ h6, .h6 {
+ font-family: $header-font-family;
+ font-style: $header-font-style;
+ font-weight: $header-font-weight;
+ color: $header-color;
+ text-rendering: $header-text-rendering;
+
+ small {
+ line-height: 0;
+ color: $header-small-font-color;
+ }
+ }
+
+ // Heading styles
+ @each $size, $headers in $header-styles {
+ @include breakpoint($size) {
+ @each $header, $header-defs in $headers {
+ $font-size-temp: 1rem;
+ #{$header}, .#{$header} {
+
+ @if map-has-key($header-defs, font-size) {
+ $font-size-temp: rem-calc(map-get($header-defs, font-size));
+ font-size: $font-size-temp;
+ } @else if map-has-key($header-defs, fs) {
+ $font-size-temp: rem-calc(map-get($header-defs, fs));
+ font-size: $font-size-temp;
+ } @else if $size == $-zf-zero-breakpoint {
+ font-size: $font-size-temp;
+ }
+ @if map-has-key($header-defs, line-height) {
+ line-height: unitless-calc(map-get($header-defs, line-height), $font-size-temp);
+ } @else if map-has-key($header-defs, lh) {
+ line-height: unitless-calc(map-get($header-defs, lh), $font-size-temp);
+ } @else if $size == $-zf-zero-breakpoint {
+ line-height: unitless-calc($header-lineheight, $font-size-temp);
+ }
+
+ @if map-has-key($header-defs, margin-top) {
+ margin-top: rem-calc(map-get($header-defs, margin-top));
+ } @else if map-has-key($header-defs, mt) {
+ margin-top: rem-calc(map-get($header-defs, mt));
+ } @else if $size == $-zf-zero-breakpoint {
+ margin-top: 0;
+ }
+ @if map-has-key($header-defs, margin-bottom) {
+ margin-bottom: rem-calc(map-get($header-defs, margin-bottom));
+ } @else if map-has-key($header-defs, mb) {
+ margin-bottom: rem-calc(map-get($header-defs, mb));
+ } @else if $size == $-zf-zero-breakpoint {
+ margin-bottom: rem-calc($header-margin-bottom);
+ }
+ }
+ }
+ }
+ }
+
+ // Links
+ a {
+ line-height: inherit;
+ color: $anchor-color;
+ text-decoration: $anchor-text-decoration;
+
+ cursor: pointer;
+
+ &:hover,
+ &:focus {
+ color: $anchor-color-hover;
+ @if $anchor-text-decoration-hover != $anchor-text-decoration {
+ text-decoration: $anchor-text-decoration-hover;
+ }
+ }
+
+ img {
+ border: 0;
+ }
+ }
+
+ // Horizontal rule
+ hr {
+ clear: both;
+
+ max-width: $hr-width;
+ height: 0;
+ margin: $hr-margin;
+
+ border-top: 0;
+ border-right: 0;
+ border-bottom: $hr-border;
+ border-left: 0;
+ }
+
+ // Lists
+ ul,
+ ol,
+ dl {
+ margin-bottom: $list-margin-bottom;
+ list-style-position: $list-style-position;
+ line-height: $list-lineheight;
+ }
+
+ // List items
+ li {
+ font-size: inherit;
+ }
+
+ // Unordered lists
+ ul {
+ margin-#{$global-left}: $list-side-margin;
+ list-style-type: $list-style-type;
+ }
+
+ // Ordered lists
+ ol {
+ margin-#{$global-left}: $list-side-margin;
+ }
+
+ // Nested unordered/ordered lists
+ ul, ol {
+ & & {
+ margin-#{$global-left}: $list-nested-side-margin;
+ margin-bottom: 0;
+ }
+ }
+
+ // Definition lists
+ dl {
+ margin-bottom: $defnlist-margin-bottom;
+
+ dt {
+ margin-bottom: $defnlist-term-margin-bottom;
+ font-weight: $defnlist-term-weight;
+ }
+ }
+
+ // Blockquotes
+ blockquote {
+ margin: 0 0 $paragraph-margin-bottom;
+ padding: $blockquote-padding;
+ border-#{$global-left}: $blockquote-border;
+
+ &, p {
+ line-height: $paragraph-lineheight;
+ color: $blockquote-color;
+ }
+ }
+
+ // Inline Citations
+ @if ($enable-cite-block == true) {
+ cite {
+ // Extending a class is not recommended.
+ // TODO: Break the typography-base/typography-helpers separation
+ @extend .cite-block;
+ }
+ }
+
+ // Abbreviations
+ abbr, abbr[title] {
+ border-bottom: $abbr-underline;
+ cursor: help;
+ text-decoration: none;
+ }
+
+ // Figures
+ figure {
+ margin: 0;
+ }
+
+ // Code
+ @if ($enable-code-inline == true) {
+ code {
+ @extend .code-inline;
+ }
+ }
+
+ // Keystrokes
+ kbd {
+ margin: 0;
+ padding: $keystroke-padding;
+
+ background-color: $keystroke-background;
+
+ font-family: $keystroke-font;
+ color: $keystroke-color;
+
+ @if has-value($keystroke-radius) {
+ border-radius: $keystroke-radius;
+ }
+ }
+}
diff --git a/src/foundation/typography/_helpers.scss b/src/foundation/typography/_helpers.scss
new file mode 100644
index 0000000..b2c8dd4
--- /dev/null
+++ b/src/foundation/typography/_helpers.scss
@@ -0,0 +1,180 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group typography-helpers
+////
+
+/// Default font size for lead paragraphs.
+/// @type Number
+$lead-font-size: $global-font-size * 1.25 !default;
+
+/// Default line height for lead paragraphs.
+/// @type String
+$lead-lineheight: 1.6 !default;
+
+/// Default line height for subheaders.
+/// @type Number
+$subheader-lineheight: 1.4 !default;
+
+/// Default font color for subheaders.
+/// @type Color
+$subheader-color: $dark-gray !default;
+
+/// Default font weight for subheaders.
+/// @type String
+$subheader-font-weight: $global-weight-normal !default;
+
+/// Default top margin for subheaders.
+/// @type Number
+$subheader-margin-top: 0.2rem !default;
+
+/// Default bottom margin for subheaders.
+/// @type Number
+$subheader-margin-bottom: 0.5rem !default;
+
+/// Default font size for statistic numbers.
+/// @type Number
+$stat-font-size: 2.5rem !default;
+
+/// Text color for `.cite-block` component.
+/// @type Color
+$cite-color: $dark-gray !default;
+
+/// Font size for `.cite-block` component.
+/// @type Number
+$cite-font-size: rem-calc(13) !default;
+
+/// Pseudo content for `.cite-block` component.
+/// @type String
+$cite-pseudo-content: '\2014 \0020' !default;
+
+/// Text color of `.code-inline` and `.code-block` components.
+/// @type Color
+$code-color: $black !default;
+
+/// Font family of `.code-inline` and `.code-block` components.
+/// @type String | List
+$code-font-family: $font-family-monospace !default;
+
+/// Font weight of text in `.code-inline` and `.code-block` components.
+/// @type String
+$code-font-weight: $global-weight-normal !default;
+
+/// Background color of `.code-inline` and `.code-block` components.
+/// @type Color
+$code-background: $light-gray !default;
+
+/// Border around `.code-inline` and `.code-block` components.
+/// @type List
+$code-border: 1px solid $medium-gray !default;
+
+/// Padding around text of the `.code-inline` component.
+/// @type Number | List
+$code-padding: rem-calc(2 5 1) !default;
+
+/// Padding around text of the `.code-block` component.
+/// @type Number | List
+$code-block-padding: 1rem !default;
+
+/// Margin under the `.code-block` component.
+/// @type Number
+$code-block-margin-bottom: 1.5rem !default;
+
+@mixin cite-block {
+ display: block;
+ color: $cite-color;
+ font-size: $cite-font-size;
+
+ &:before {
+ content: $cite-pseudo-content;
+ }
+}
+
+/// Add basic styles for a code helper.
+/// See `code-inline` and `code-block` mixins.
+@mixin code-style {
+ border: $code-border;
+ background-color: $code-background;
+
+ font-family: $code-font-family;
+ font-weight: $code-font-weight;
+ color: $code-color;
+}
+
+/// Make code helper from the `code-style` mixin inline.
+/// Used to generate `.code-inline`
+@mixin code-inline {
+ display: inline;
+ max-width: 100%;
+ word-wrap: break-word;
+
+ padding: $code-padding;
+}
+
+/// Make code helper from the `code-style` mixin a block.
+/// Used to generate `.code-block`
+@mixin code-block {
+ display: block;
+ overflow: auto;
+ white-space: pre;
+
+ padding: $code-block-padding;
+ margin-bottom: $code-block-margin-bottom;
+}
+
+@mixin foundation-typography-helpers {
+ // Use to create a subheading under a main header
+ // Make sure you pair the two elements in a <header> element, like this:
+ // <header>
+ // <h1>Heading</h1>
+ // <h2>Subheading</h2>
+ // </header>
+ .subheader {
+ margin-top: $subheader-margin-top;
+ margin-bottom: $subheader-margin-bottom;
+
+ font-weight: $subheader-font-weight;
+ line-height: $subheader-lineheight;
+ color: $subheader-color;
+ }
+
+ // Use to style an introductory lead, deck, blurb, etc.
+ .lead {
+ font-size: $lead-font-size;
+ line-height: $lead-lineheight;
+ }
+
+ // Use to style a large number to display a statistic
+ .stat {
+ font-size: $stat-font-size;
+ line-height: 1;
+
+ p + & {
+ margin-top: -1rem;
+ }
+ }
+
+ ul, ol {
+ // Use to remove numbers from ordered list & bullets from unordered list
+ &.no-bullet {
+ margin-#{$global-left}: 0;
+ list-style: none;
+ }
+ }
+
+ .cite-block {
+ @include cite-block;
+ }
+
+ .code-inline {
+ @include code-style;
+ @include code-inline;
+ }
+
+ .code-block {
+ @include code-style;
+ @include code-block;
+ }
+}
diff --git a/src/foundation/typography/_print.scss b/src/foundation/typography/_print.scss
new file mode 100644
index 0000000..5088c60
--- /dev/null
+++ b/src/foundation/typography/_print.scss
@@ -0,0 +1,96 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+/// If `true`, all elements will have transparent backgrounds when printed, to save on ink.
+/// @type Boolean
+/// @group global
+$print-transparent-backgrounds: true !default;
+
+/// If `true`, displays next to all links their "href" when printed.
+/// @type Boolean
+/// @group global
+$print-hrefs: true !default;
+
+// sass-lint:disable-all
+
+@mixin foundation-print-styles {
+ .show-for-print { display: none !important; }
+
+ @media print {
+ * {
+ // Ensure a "black-on-white" print by removing backgrounds,
+ // using black text everywhere and forcing the browser to economize ink.
+ @if $print-transparent-backgrounds {
+ background: transparent !important;
+ color: black !important; // Black prints faster: h5bp.com/s
+ color-adjust: economy;
+ }
+ // Otherwise, prevent any economy by the browser.
+ @else {
+ color-adjust: exact;
+ }
+
+ box-shadow: none !important;
+ text-shadow: none !important;
+ }
+
+ .show-for-print { display: block !important; }
+ .hide-for-print { display: none !important; }
+
+ table.show-for-print { display: table !important; }
+ thead.show-for-print { display: table-header-group !important; }
+ tbody.show-for-print { display: table-row-group !important; }
+ tr.show-for-print { display: table-row !important; }
+ td.show-for-print { display: table-cell !important; }
+ th.show-for-print { display: table-cell !important; }
+
+ // Display the URL of a link after the text
+ a,
+ a:visited { text-decoration: underline;}
+ @if $print-hrefs {
+ a[href]:after { content: ' (' attr(href) ')'; }
+ }
+
+ // Don't display the URL for images or JavaScript/internal links
+ .ir a:after,
+ a[href^='javascript:']:after,
+ a[href^='#']:after { content: ''; }
+
+ // Display what an abbreviation stands for after the text
+ abbr[title]:after { content: ' (' attr(title) ')'; }
+
+ // Prevent page breaks in the middle of a blockquote or preformatted text block
+ pre,
+ blockquote {
+ border: 1px solid $dark-gray;
+ page-break-inside: avoid;
+ }
+
+ // h5bp.com/t
+ thead { display: table-header-group; }
+
+ tr,
+ img { page-break-inside: avoid; }
+
+ img { max-width: 100% !important; }
+
+ @page { margin: 0.5cm; }
+
+ p,
+ h2,
+ h3 {
+ orphans: 3;
+ widows: 3;
+ }
+
+ // Avoid page breaks after a heading
+ h2,
+ h3 { page-break-after: avoid; }
+
+ // Helper to re-allow page breaks in the middle of certain elements (e.g. pre, blockquote, tr)
+ .print-break-inside {
+ page-break-inside: auto;
+ }
+ }
+}
diff --git a/src/foundation/typography/_typography.scss b/src/foundation/typography/_typography.scss
new file mode 100644
index 0000000..c794126
--- /dev/null
+++ b/src/foundation/typography/_typography.scss
@@ -0,0 +1,26 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group typography
+////
+
+// Base typography styles (tags only)
+@import 'base';
+
+// Typography helper classes (classes only)
+@import 'helpers';
+
+// Text alignment classes
+@import 'alignment';
+
+// Print styles
+@import 'print';
+
+@mixin foundation-typography {
+ @include foundation-typography-base;
+ @include foundation-typography-helpers;
+ @include foundation-text-alignment;
+ @include foundation-print-styles;
+}
diff --git a/src/foundation/util/_breakpoint.scss b/src/foundation/util/_breakpoint.scss
new file mode 100644
index 0000000..9c2f818
--- /dev/null
+++ b/src/foundation/util/_breakpoint.scss
@@ -0,0 +1,435 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group breakpoints
+////
+
+/// A list of named breakpoints. You can use these with the `breakpoint()` mixin to quickly create media queries.
+/// @type Map
+$breakpoints: (
+ small: 0,
+ medium: 640px,
+ large: 1024px,
+ xlarge: 1200px,
+ xxlarge: 1440px,
+) !default;
+
+/// A list of named HiDPI breakpoints. You can use these with the `breakpoint()` mixin to quickly create media queries for resolutions.
+/// Values must represent the device pixels / web pixels ration and be unitless or in DPPX.
+/// @type Map
+$breakpoints-hidpi: (
+ hidpi-1: 1,
+ hidpi-1-5: 1.5,
+ hidpi-2: 2,
+ retina: 2,
+ hidpi-3: 3
+) !default;
+
+/// The largest named breakpoint in which to include print as a media type
+/// @type Keyword
+$print-breakpoint: large !default;
+
+$-zf-zero-breakpoint: small !default;
+
+$-zf-breakpoints-keys: map-to-list($breakpoints, 'keys');
+
+@if nth(map-values($breakpoints), 1) != 0 {
+ @error 'The first key in the $breakpoints map must have a value of "0".';
+}
+@else {
+ $-zf-zero-breakpoint: nth(map-keys($breakpoints), 1);
+}
+
+/// All of the names in this list will be output as classes in your CSS, like `.small-12`, `.medium-6`, and so on. Each value in this list must also be in the `$breakpoints` map.
+/// @type List
+$breakpoint-classes: (small medium large) !default;
+
+/// Generates a media query string matching the input value. Refer to the documentation for the `breakpoint()` mixin to see what the possible inputs are.
+///
+/// @param {Keyword|Number} $val [small] - Breakpoint name, or px, rem, or em value to process.
+@function breakpoint($val: $-zf-zero-breakpoint) {
+ // Web standard Pixels per inch. (1ddpx / $std-web-dpi) = 1dpi
+ // See https://www.w3.org/TR/css-values-3/#absolute-lengths
+ $std-web-dpi: 96;
+
+ // Size or keyword
+ $bp: nth($val, 1);
+ // Value of the following breakpoint
+ $bp-next: null;
+ // Value for max-width media queries
+ $bp-min: null;
+ // Value for min-width media queries
+ $bp-max: null;
+ // Direction of media query (up, down, or only)
+ $dir: if(length($val) > 1, nth($val, 2), up);
+ // If named, name of the breakpoint
+ $name: null;
+ // If the breakpoint is a HiDPI breakpoint
+ $hidpi: false;
+
+ // Orientation media queries have a unique syntax
+ @if $bp == 'landscape' or $bp == 'portrait' {
+ @return '(orientation: #{$bp})';
+ }
+
+ // If a breakpoint name is given, get its value from the $breakpoints/$breakpoints-hidpi map.
+ @if type-of($bp) == 'string' {
+ @if map-has-key($breakpoints, $bp) {
+ $name: $bp;
+ $bp: map-get($breakpoints, $name);
+ $bp-next: -zf-map-next($breakpoints, $name);
+ }
+ @else if map-has-key($breakpoints-hidpi, $bp) {
+ $name: $bp;
+ $bp: map-get($breakpoints-hidpi, $name);
+ $bp-next: -zf-map-next-number($breakpoints-hidpi, $bp);
+ $hidpi: true;
+ }
+ @else {
+ $bp: 0;
+ @warn 'breakpoint(): "#{$val}" is not defined in your `$breakpoints` or `$breakpoints-hidpi` setting.';
+ }
+ }
+
+ @if not $name and $dir == 'only' {
+ @warn 'breakpoint(): Only named media queries can have an `only` range.';
+ @return null;
+ }
+
+ // Only 'only' and 'up' have a min limit.
+ @if $dir == 'only' or $dir == 'up' {
+ $bp-min: if($hidpi, strip-unit($bp), -zf-bp-to-em($bp));
+ }
+ // Only 'only' and 'down' have a max limit.
+ @if $dir == 'only' or $dir == 'down' {
+ // If the breakpoint is a value, use it as max limit.
+ @if not $name {
+ $bp-max: if($hidpi, strip-unit($bp), -zf-bp-to-em($bp));
+ }
+ // If the breakpoint is named, the max limit is the following breakpoint - 1px.
+ @else if $bp-next {
+ // Max value is 0.2px under the next breakpoint (0.02 / 16 = 0.00125).
+ // Use a precision under 1px to support browser zoom, but not to low to avoid rounding.
+ // See https://github.com/zurb/foundation-sites/issues/11313
+ $bp-max: if($hidpi, $bp-next - (1/$std-web-dpi), -zf-bp-to-em($bp-next) - 0.00125);
+ }
+ }
+
+ // Generate the media query string from min and max limits.
+ @if $hidpi {
+ // Generate values in DPI instead of DPPX for an IE9-11/Opera mini compatibility.
+ // See https://caniuse.com/#feat=css-media-resolution
+ $bp-min-dpi: if($bp-min, $bp-min * $std-web-dpi * 1dpi, $bp-min);
+ $bp-max-dpi: if($bp-max, $bp-max * $std-web-dpi * 1dpi, $bp-max);
+ @return zf-str-join(
+ -zf-bp-join($bp-min, $bp-max, '-webkit-min-device-pixel-ratio', '-webkit-max-device-pixel-ratio'),
+ -zf-bp-join($bp-min-dpi, $bp-max-dpi, 'min-resolution', 'max-resolution'),
+ ', ');
+ }
+ @else {
+ @return -zf-bp-join($bp-min, $bp-max);
+ }
+}
+
+/// Wraps a media query around the content you put inside the mixin. This mixin accepts a number of values:
+/// - If a string is passed, the mixin will look for it in the `$breakpoints` and `$breakpoints-hidpi` maps, and use a media query there.
+/// - If a pixel value is passed, it will be converted to an em value using `$global-font-size` as the base.
+/// - If a rem value is passed, the unit will be changed to em.
+/// - If an em value is passed, the value will be used as-is.
+///
+/// If multiple values are passed, the mixin will generate a media query for each of them as described above.
+/// Since the content is duplicated for each breakpoint, this mixin should only be used with properties that
+/// change across breakpoints.
+///
+/// @param {Keyword|Number} $values... - Breakpoint name or px/rem/em value to process.
+///
+/// @output If the breakpoint is "0px and larger", outputs the content as-is. Otherwise, outputs the content wrapped in a media query.
+@mixin breakpoint($values...) {
+ @for $i from 1 through length($values) {
+ $value: nth($values, $i);
+ $str: breakpoint($value);
+ $bp: index($-zf-breakpoints-keys, nth($value, 1));
+ $pbp: index($-zf-breakpoints-keys, $print-breakpoint);
+ // Direction of media query (up, down, or only)
+ $dir: if(length($value) > 1, nth($value, 2), up);
+
+ $old-zf-size: null;
+
+ // Make breakpoint size available as a variable
+ @if global-variable-exists(-zf-size) {
+ $old-zf-size: $-zf-size;
+ }
+ $-zf-size: nth($value, 1) !global; // get the first value to account for `only` and `down` keywords
+
+ // If $str is still an empty string, no media query is needed
+ @if $str == '' {
+ @content;
+ }
+
+ // Otherwise, wrap the content in a media query
+ @else {
+ // For named breakpoints less than or equal to $print-breakpoint, add print to the media types
+ // generate print if the breakpoint affects the print-breakpoint (or smaller).
+ // This means the current condition only needs to be extended so 'down' always generates print.
+ @if $bp != null and ($bp <= $pbp or $dir == down) {
+ @media print, screen and #{$str} {
+ @content;
+ }
+ }
+ @else {
+ @media screen and #{$str} {
+ @content;
+ }
+ }
+ }
+
+ $-zf-size: $old-zf-size !global;
+ }
+}
+
+/// Converts the breakpoints map to a URL-encoded string, like this: `key1=value1&key2=value2`. The value is then dropped into the CSS for a special `<meta>` tag, which is read by the Foundation JavaScript. This is how we transfer values from Sass to JavaScript, so they can be defined in one place.
+/// @access private
+///
+/// @param {Map} $map - Map to convert.
+///
+/// @returns {String} A string containing the map's contents.
+@function -zf-bp-serialize($map) {
+ $str: '';
+ @each $key, $value in $map {
+ $str: $str + $key + '=' + -zf-bp-to-em($value) + '&';
+ }
+ $str: str-slice($str, 1, -2);
+
+ @return $str;
+}
+
+/// Find the next key in a map.
+/// @access private
+///
+/// @param {Map} $map - Map to traverse.
+/// @param {Mixed} $key - Key to use as a starting point.
+///
+/// @returns {Mixed} The value for the key after `$key`, if `$key` was found. If `$key` was not found, or `$key` was the last value in the map, returns `null`.
+@function -zf-map-next($map, $key) {
+
+ // Store the keys of the map as a list
+ $values: map-keys($map);
+
+ $i: 0;
+
+ // If the Key Exists, Get the index of the key within the map and add 1 to it for the next breakpoint in the map
+ @if (map-has-key($map, $key)) {
+ $i: index($values, $key) + 1;
+ }
+
+ // If the key doesn't exist, or it's the last key in the map, return null
+ @if ($i > length($map) or $i == 0) {
+ @return null;
+ }
+ // Otherwise, return the value
+ @else {
+ @return map-get($map, nth($values, $i));
+ }
+
+}
+
+/// Find the next number in a map.
+/// @access private
+///
+/// @param {Map} $map - Map to traverse.
+/// @param {Mixed} $number - Number to use as a starting point.
+///
+/// @returns {Mixed} The number following `$number`, if `$number` was found. If `$number` was not found, or `$number` was the biggest number in the map, returns `null`.
+@function -zf-map-next-number($map, $number) {
+
+ $next_number: null;
+
+ @each $k, $v in $map {
+ @if type-of($v) == 'number' and $v > $number and ($next_number == null or $v < $next_number) {
+ $next_number: $v;
+ }
+ }
+
+ @return $next_number;
+}
+
+/// Return a list of our named breakpoints less than $key. Useful for dealing with
+/// responsive gutters for the grid.
+/// @access private
+///
+/// @param {String} $key - Key to use as last breakpoint.
+///
+/// @returns {Array} The list of breakpoints up to and. If $key is auto, returns breakpoints above the zero
+@function -zf-breakpoints-less-than($key) {
+ $list: ();
+ $found_key: false;
+
+ @each $name in $-zf-breakpoints-keys {
+ @if ($name == $key) {
+ $found_key: true;
+ }
+ @if not $found_key {
+ $list: append($list, $name);
+ }
+ }
+ @return $list;
+}
+
+/// Return a list of our named breakpoints less than $key. Useful for dealing with
+/// responsive gutters for the grid.
+/// @access private
+///
+/// @param {String} $breakpoint - a named or non-named breakpoint.
+///
+/// @returns {Array} The list of breakpoints up to and. If $key is auto, returns breakpoints above the zero
+@function -zf-closest-named-breakpoint($breakpoint) {
+ $last: $-zf-zero-breakpoint;
+ $found: false;
+
+ $value: unitless-calc($breakpoint, 1px);
+ @each $key, $val in $breakpoints {
+ @if not $found {
+ @if unitless-calc($val) > $value {
+ $found: true;
+ } @else {
+ $last: $key;
+ }
+ }
+ }
+
+ @return $last;
+}
+
+/// Get a value for a breakpoint from a responsive config map or single value.
+/// - If the config is a single value, return it regardless of `$value`.
+/// - If the config is a map and has the key `$value`, the exact breakpoint value is returned.
+/// - If the config is a map and does *not* have the breakpoint, the value matching the next lowest breakpoint in the config map is returned.
+/// @access private
+///
+/// @param {Number|Map} $map - Responsive config map or single value.
+/// @param {Keyword} $value - Breakpoint name to use.
+///
+/// @return {Mixed} The corresponding breakpoint value.
+@function -zf-get-bp-val($map, $value) {
+ // If the given map is a single value, return it
+ @if type-of($map) == 'number' {
+ @return $map;
+ }
+
+
+ // Check if the breakpoint name exists globally
+ @if not map-has-key($breakpoints, $value) {
+ @if type-of($value) == 'number' {
+ $value: -zf-closest-named-breakpoint($value);
+ } @else {
+ @return null;
+ }
+ }
+ // Check if the breakpoint name exists in the local config map
+ @else if map-has-key($map, $value) {
+ // If it does, just return the value
+ @return map-get($map, $value);
+ }
+ // Otherwise, find the next lowest breakpoint and return that value
+ @else {
+ $anchor: null;
+ $found: false;
+
+ @each $key, $val in $breakpoints {
+ @if not $found {
+ @if map-has-key($map, $key) {
+ $anchor: $key;
+ }
+ @if $key == $value {
+ $found: true;
+ }
+ }
+ }
+
+ @return map-get($map, $anchor);
+ }
+}
+
+/// Return the best breakpoint to use according to the calling context. It returns in order:
+/// 1. the given `$value` argument if it is not null.
+/// 2. the global breakpoint context `$-zf-size` if it is not null (like if called inside then `breakpoint()` mixin)
+/// 3. the given `$default` argument.
+/// @access private
+///
+/// @param {Keyword} $value [null] - Breakpoint to use in priority if non-null.
+/// @param {Keyword} $default [null] - Breakpoint to use by default if no other value can be used.
+///
+/// @return {Keyword} The resolved breakpoint.
+@function -zf-current-breakpoint($value: null, $default: null) {
+ @if ($value != null) {
+ @return $value;
+ }
+ @else if (variable-exists(-zf-size) and type-of($-zf-size) != 'number') and $-zf-size != null {
+ @return $-zf-size;
+ }
+ @else {
+ @return $default;
+ }
+}
+
+/// Return media query string from the given min and/or max limits.
+/// If a limit is equal to `null` or `0`, it is ignored.
+/// @access private
+///
+/// @param {Number} $min [0] - Min media query limit.
+/// @param {Number} $max [0] - Max media query limit.
+/// @param {String} $min-name ['min-width'] - Name of the min media query limit.
+/// @param {String} $delimiter ['max-width'] - Name of the max media query limit.
+///
+/// @returns {String} Media Query string.
+@function -zf-bp-join(
+ $min: 0,
+ $max: 0,
+ $min-name: 'min-width',
+ $max-name: 'max-width'
+) {
+ @return zf-str-join(
+ if($min and $min > 0, '(#{$min-name}: #{$min})', null),
+ if($max and $max > 0, '(#{$max-name}: #{$max})', null),
+ ' and ');
+}
+
+$small-up: '';
+$small-only: '';
+
+@if map-has-key($breakpoints, small) {
+ $small-up: screen;
+ $small-only: unquote('screen and #{breakpoint(small only)}');
+}
+
+$medium-up: '';
+$medium-only: '';
+
+@if map-has-key($breakpoints, medium) {
+ $medium-up: unquote('screen and #{breakpoint(medium)}');
+ $medium-only: unquote('screen and #{breakpoint(medium only)}');
+}
+
+$large-up: '';
+$large-only: '';
+
+@if map-has-key($breakpoints, large) {
+ $large-up: unquote('screen and #{breakpoint(large)}');
+ $large-only: unquote('screen and #{breakpoint(large only)}');
+}
+
+$xlarge-up: '';
+$xlarge-only: '';
+
+@if map-has-key($breakpoints, xlarge) {
+ $xlarge-up: unquote('screen and #{breakpoint(xlarge)}');
+ $xlarge-only: unquote('screen and #{breakpoint(xlarge only)}');
+}
+
+$xxlarge-up: '';
+
+@if map-has-key($breakpoints, xxlarge) {
+ $xxlarge-up: unquote('screen and #{breakpoint(xxlarge)}');
+}
diff --git a/src/foundation/util/_color.scss b/src/foundation/util/_color.scss
new file mode 100644
index 0000000..f88588e
--- /dev/null
+++ b/src/foundation/util/_color.scss
@@ -0,0 +1,139 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+@import 'math';
+
+$contrast-warnings: true !default;
+
+////
+/// @group functions
+////
+
+/// Checks the luminance of `$color`.
+///
+/// @param {Color} $color - Color to check the luminance of.
+///
+/// @returns {Number} The luminance of `$color`.
+@function color-luminance($color) {
+ // Adapted from: https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js
+ // Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
+ $rgba: red($color), green($color), blue($color);
+ $rgba2: ();
+
+ @for $i from 1 through 3 {
+ $rgb: nth($rgba, $i);
+ $rgb: $rgb / 255;
+
+ $rgb: if($rgb < 0.03928, $rgb / 12.92, pow(($rgb + 0.055) / 1.055, 2.4));
+
+ $rgba2: append($rgba2, $rgb);
+ }
+
+ @return 0.2126 * nth($rgba2, 1) + 0.7152 * nth($rgba2, 2) + 0.0722 * nth($rgba2, 3);
+}
+
+/// Checks the contrast ratio of two colors.
+///
+/// @param {Color} $color1 - First color to compare.
+/// @param {Color} $color2 - Second color to compare.
+///
+/// @returns {Number} The contrast ratio of the compared colors.
+@function color-contrast($color1, $color2) {
+ // Adapted from: https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js
+ // Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
+ $luminance1: color-luminance($color1) + 0.05;
+ $luminance2: color-luminance($color2) + 0.05;
+ $ratio: $luminance1 / $luminance2;
+
+ @if $luminance2 > $luminance1 {
+ $ratio: 1 / $ratio;
+ }
+
+ $ratio: round($ratio * 10) / 10;
+
+ @return $ratio;
+}
+
+/// Checks the luminance of `$base`, and returns the color from `$colors` (list of colors) that has the most contrast.
+///
+/// @param {Color} $base - Color to check luminance.
+/// @param {List} $colors [($white, $black)] - Colors to compare.
+/// @param {Number} $tolerance [$global-color-pick-contrast-tolerance] - Contrast tolerance.
+///
+/// @returns {Color} the color from `$colors` (list of colors) that has the most contrast.
+@function color-pick-contrast($base, $colors: ($white, $black), $tolerance: $global-color-pick-contrast-tolerance) {
+ $contrast: color-contrast($base, nth($colors, 1));
+ $best: nth($colors, 1);
+
+ @for $i from 2 through length($colors) {
+ $current-contrast: color-contrast($base, nth($colors, $i));
+ @if ($current-contrast - $contrast > $tolerance) {
+ $contrast: color-contrast($base, nth($colors, $i));
+ $best: nth($colors, $i);
+ }
+ }
+
+ @if ($contrast-warnings and $contrast < 3) {
+ @warn "Contrast ratio of #{$best} on #{$base} is pretty bad, just #{$contrast}";
+ }
+
+ @return $best;
+}
+
+/// Scales a color to be darker if it's light, or lighter if it's dark. Use this function to tint a color appropriate to its lightness.
+///
+/// @param {Color} $color - Color to scale.
+/// @param {Percentage} $scale [5%] - Amount to scale up or down.
+/// @param {Percentage} $threshold [40%] - Threshold of lightness to check against.
+///
+/// @returns {Color} A scaled color.
+@function smart-scale($color, $scale: 5%, $threshold: 40%) {
+ @if lightness($color) > $threshold {
+ $scale: -$scale;
+ }
+ @return scale-color($color, $lightness: $scale);
+}
+
+/// Get color from foundation-palette
+///
+/// @param {key} color key from foundation-palette
+///
+/// @returns {Color} color from foundation-palette
+@function get-color($key) {
+ @if map-has-key($foundation-palette, $key) {
+ @return map-get($foundation-palette, $key);
+ }
+ @else {
+ @error 'given $key is not available in $foundation-palette';
+ }
+}
+
+/// Transfers the colors in the `$foundation-palette` map into variables, such as `$primary-color` and `$secondary-color`. Call this mixin below the Global section of your settings file to properly migrate your codebase.
+@mixin add-foundation-colors() {
+ @if map-has-key($foundation-palette, primary) {
+ $primary-color: map-get($foundation-palette, primary) !global;
+ } @else {
+ $primary-color: #1779ba !global;
+ }
+ @if map-has-key($foundation-palette, secondary) {
+ $secondary-color: map-get($foundation-palette, secondary) !global;
+ } @else {
+ $secondary-color: #767676 !global;
+ }
+ @if map-has-key($foundation-palette, success) {
+ $success-color: map-get($foundation-palette, success) !global;
+ } @else {
+ $success-color: #3adb76 !global;
+ }
+ @if map-has-key($foundation-palette, warning) {
+ $warning-color: map-get($foundation-palette, warning) !global;
+ } @else {
+ $warning-color: #ffae00 !global;
+ }
+ @if map-has-key($foundation-palette, alert) {
+ $alert-color: map-get($foundation-palette, alert) !global;
+ } @else {
+ $alert-color: #cc4b37 !global;
+ }
+}
diff --git a/src/foundation/util/_direction.scss b/src/foundation/util/_direction.scss
new file mode 100644
index 0000000..7874ec4
--- /dev/null
+++ b/src/foundation/util/_direction.scss
@@ -0,0 +1,31 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group functions
+////
+
+/// Returns the opposite direction of $dir
+///
+/// @param {Keyword} $dir - Used direction between "top", "right", "bottom" and "left".
+/// @return {Keyword} Opposite direction of $dir
+@function direction-opposite(
+ $dir
+) {
+ $dirs: (top, right, bottom, left);
+ $place: index($dirs, $dir);
+
+ @if $place == null {
+ @error 'direction-opposite: Invalid $dir parameter, expected a value from "#{$dirs}", found "#{$dir}".';
+ @return null;
+ }
+
+ // Calculate the opposite place in a circle, with a starting index of 1
+ $length: length($dirs);
+ $demi: $length / 2;
+ $opposite-place: (($place + $demi - 1) % $length) + 1;
+
+ @return nth($dirs, $opposite-place);
+}
+
diff --git a/src/foundation/util/_flex.scss b/src/foundation/util/_flex.scss
new file mode 100644
index 0000000..2a48b6d
--- /dev/null
+++ b/src/foundation/util/_flex.scss
@@ -0,0 +1,90 @@
+@function -zf-flex-justify($text-direction){
+ $-zf-flex-justify: (
+ 'left': if($text-direction == rtl, flex-end, flex-start),
+ 'right': if($text-direction == rtl, flex-start, flex-end),
+ 'center': center,
+ 'justify': space-between,
+ 'spaced': space-around,
+ );
+
+ @return $-zf-flex-justify;
+}
+
+
+$-zf-flex-align: (
+ 'top': flex-start,
+ 'bottom': flex-end,
+ 'middle': center,
+ 'stretch': stretch,
+);
+
+$-zf-flex-direction: (
+ 'row': row,
+ 'row-reverse': row-reverse,
+ 'column': column,
+ 'column-reverse': column-reverse,
+);
+
+/// Enables flexbox by adding `display: flex` to the element.
+@mixin flex {
+ display: flex;
+}
+
+/// Horizontally or vertically aligns the items within a flex container.
+///
+/// @param {Keyword} $x [null] - Horizontal alignment to use. Can be `left`, `right`, `center`, `justify`, or `spaced`. Or, set it to `null` (the default) to not set horizontal alignment.
+/// @param {Keyword} $y [null] - Vertical alignment to use. Can be `top`, `bottom`, `middle`, or `stretch`. Or, set it to `null` (the default) to not set vertical alignment.
+@mixin flex-align($x: null, $y: null) {
+ @if $x {
+ @if map-has-key($-zf-flex-justify, $x) {
+ $x: map-get($-zf-flex-justify, $x);
+ }
+ @else {
+ @warn 'flex-grid-row-align(): #{$x} is not a valid value for horizontal alignment. Use left, right, center, justify, or spaced.';
+ }
+ }
+
+ @if $y {
+ @if map-has-key($-zf-flex-align, $y) {
+ $y: map-get($-zf-flex-align, $y);
+ }
+ @else {
+ @warn 'flex-grid-row-align(): #{$y} is not a valid value for vertical alignment. Use top, bottom, middle, or stretch.';
+ }
+ }
+
+ justify-content: $x;
+ align-items: $y;
+}
+
+/// Vertically align a single column within a flex row. Apply this mixin to a flex column.
+///
+/// @param {Keyword} $y [null] - Vertical alignment to use. Can be `top`, `bottom`, `middle`, or `stretch`. Or, set it to `null` (the default) to not set vertical alignment.
+@mixin flex-align-self($y: null) {
+ @if $y {
+ @if map-has-key($-zf-flex-align, $y) {
+ $y: map-get($-zf-flex-align, $y);
+ }
+ @else {
+ @warn 'flex-grid-column-align(): #{$y} is not a valid value for alignment. Use top, bottom, middle, or stretch.';
+ }
+ }
+
+ align-self: $y;
+}
+
+/// Changes the source order of a flex child. Children with lower numbers appear first in the layout.
+/// @param {Number} $order [0] - Order number to apply.
+@mixin flex-order($order: 0) {
+ order: $order;
+}
+
+/// Change flex-direction
+/// @param {Keyword} $direction [row] - Flex direction to use. Can be
+/// - row (default): same as text direction
+/// - row-reverse: opposite to text direction
+/// - column: same as row but top to bottom
+/// - column-reverse: same as row-reverse top to bottom
+@mixin flex-direction($direction: row) {
+ flex-direction: $direction;
+}
diff --git a/src/foundation/util/_math.scss b/src/foundation/util/_math.scss
new file mode 100644
index 0000000..947a576
--- /dev/null
+++ b/src/foundation/util/_math.scss
@@ -0,0 +1,147 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group functions
+////
+
+/// Finds the greatest common divisor of two integers.
+///
+/// @param {Number} $a - First number to compare.
+/// @param {Number} $b - Second number to compare.
+///
+/// @returns {Number} The greatest common divisor.
+@function gcd($a, $b) {
+ // From: http://rosettacode.org/wiki/Greatest_common_divisor#JavaScript
+ @if ($b != 0) {
+ @return gcd($b, $a % $b);
+ }
+ @else {
+ @return abs($a);
+ }
+}
+
+/// Handles decimal exponents by trying to convert them into a fraction and then use a nth-root-algorithm for parts of the calculation
+///
+/// @param {Number} $base - The base number.
+/// @param {Number} $exponent - The exponent.
+///
+/// @returns {Number} The product of the exponentiation.
+@function pow($base, $exponent, $prec: 16) {
+ @if (floor($exponent) != $exponent) {
+ $prec2 : pow(10, $prec);
+ $exponent: round($exponent * $prec2);
+ $denominator: gcd($exponent, $prec2);
+ @return nth-root(pow($base, $exponent / $denominator), $prec2 / $denominator, $prec);
+ }
+
+ $value: $base;
+ @if $exponent > 1 {
+ @for $i from 2 through $exponent {
+ $value: $value * $base;
+ }
+ }
+ @else if $exponent < 1 {
+ @for $i from 0 through -$exponent {
+ $value: $value / $base;
+ }
+ }
+
+ @return $value;
+}
+
+@function nth-root($num, $n: 2, $prec: 12) {
+ // From: http://rosettacode.org/wiki/Nth_root#JavaScript
+ $x: 1;
+
+ @for $i from 0 through $prec {
+ $x: 1 / $n * (($n - 1) * $x + ($num / pow($x, $n - 1)));
+ }
+
+ @return $x;
+}
+
+/// Calculates the height as a percentage of the width for a given ratio.
+/// @param {List} $ratio - Ratio to use to calculate the height, formatted as `x by y`.
+/// @return {Number} A percentage value for the height relative to the width of a responsive container.
+@function ratio-to-percentage($ratio) {
+ $w: nth($ratio, 1);
+ $h: nth($ratio, 3);
+ @return $h / $w * 100%;
+}
+
+/// Parse the given `$fraction` to numerators and denumerators.
+///
+/// @param {*} $fraction - Value representing a fraction to parse. It can be formatted as `50%`, `1 of 2`, `1/2` or `50` (no denominator would be returned).
+///
+/// @return {List} List of parsed values with numerator at first position and denumerator as second. These values may be null.
+@function zf-parse-fraction($fraction) {
+
+ @if type-of($fraction) == 'number' {
+ // "50%"
+ @if unit($fraction) == '%' {
+ @return (strip-unit($fraction), 100);
+ }
+ @else if (unit($fraction) == '') {
+ // "0.5"
+ @if $fraction < 1 {
+ @return ($fraction * 100, 100);
+ }
+ // "50"
+ @else {
+ @return ($fraction, null);
+ }
+ }
+ }
+
+ @else if type-of($fraction) == 'list' {
+ // "50 of 100", "50/100"...
+ @if length($fraction) == 3
+ and type-of(nth($fraction, 1) == 'number')
+ and type-of(nth($fraction, 3) == 'number') {
+ @return (nth($fraction, 1), nth($fraction, 3));
+ }
+ }
+
+ @return (null, null);
+}
+
+/// Returns whether the given `$value` represents a fraction. Supports formats like `50%`, `1 of 2`, `1 per 2` or `1/2`.
+///
+/// @param {*} $value - Value to test.
+/// @param {Boolean} $allow-no-denominator [false] - If `true`, simple numbers without denominators like `50` are supported.
+///
+/// @return {Boolean} `true` if `$value` represents a fraction, `false` otherwise.
+@function zf-is-fraction($value, $allow-no-denominator: false) {
+ $parsed: zf-parse-fraction($value);
+ @return not(nth($parsed, 1) == null
+ or (nth($parsed, 2) == null and $allow-no-denominator == false));
+}
+
+/// Calculate a percentage from a given fraction.
+///
+/// @param {Number|List} $fraction - Value representing a fraction to use to calculate the percentage, formatted as `50` (relative to `$denominator`), `50%`, `1 of 2` or `1/2`.
+/// @param {Number|List} $denominator - Default value to use as denominator when `$fraction` represents an absolute value.
+@function fraction-to-percentage(
+ $fraction,
+ $denominator: null
+) {
+ $parsed: zf-parse-fraction($fraction);
+ $parsed-nominator: nth($parsed, 1);
+ $parsed-denominator: nth($parsed, 2);
+
+ @if $parsed-nominator == null {
+ @error 'Wrong syntax for "fraction-to-percentage()". Use a number, decimal, percentage, or "n of n" / "n/n".';
+ }
+ @if $parsed-denominator == null {
+ @if type-of($denominator) == 'number' {
+ $parsed-denominator: $denominator;
+ }
+ @else {
+ @error 'Error with "fraction-to-percentage()". A default "$denominator" is required to support absolute values';
+ }
+ }
+
+ @return percentage($parsed-nominator / $parsed-denominator);
+}
diff --git a/src/foundation/util/_mixins.scss b/src/foundation/util/_mixins.scss
new file mode 100644
index 0000000..c256dc6
--- /dev/null
+++ b/src/foundation/util/_mixins.scss
@@ -0,0 +1,373 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group functions
+////
+
+/// Creates an inner box-shadow for only one side
+///
+/// @param {Keyword} $side - Side the shadow is supposed to appear. Can be `top`, `left`, `right` or `bottom`.
+/// @param {Number} $size - Width for the target side.
+/// @param {Color} $color - Color of the shadow.
+@mixin inner-side-shadow(
+ $side: bottom,
+ $size: 20px,
+ $color: rgba($black, 0.25)
+) {
+
+ $helper: round($size * 0.65);
+
+ @if ($side == top) {
+ box-shadow: inset 0 $helper $size (-1)*$helper $color;
+ } @else if ($side == left) {
+ box-shadow: inset $helper 0 $size (-1)*$helper $color;
+ } @else if ($side == right) {
+ box-shadow: inset (-1)*$helper 0 $size (-1)*$helper $color;
+ } @else if ($side == bottom) {
+ box-shadow: inset 0 (-1)*$helper $size (-1)*$helper $color;
+ }
+}
+
+/// Creates a CSS triangle, which can be used for dropdown arrows, dropdown pips, and more. Use this mixin inside a `&::before` or `&::after` selector, to attach the triangle to an existing element.
+///
+/// @param {Number} $triangle-size - Width of the triangle.
+/// @param {Color} $triangle-color - Color of the triangle.
+/// @param {Keyword} $triangle-direction - Direction the triangle points. Can be `up`, `right`, `down`, or `left`.
+@mixin css-triangle(
+ $triangle-size,
+ $triangle-color,
+ $triangle-direction
+) {
+ display: block;
+ width: 0;
+ height: 0;
+
+ border: inset $triangle-size;
+
+ content: '';
+
+ @if ($triangle-direction == down) {
+ border-bottom-width: 0;
+ border-top-style: solid;
+ border-color: $triangle-color transparent transparent;
+ }
+ @if ($triangle-direction == up) {
+ border-top-width: 0;
+ border-bottom-style: solid;
+ border-color: transparent transparent $triangle-color;
+ }
+ @if ($triangle-direction == right) {
+ border-right-width: 0;
+ border-left-style: solid;
+ border-color: transparent transparent transparent $triangle-color;
+ }
+ @if ($triangle-direction == left) {
+ border-left-width: 0;
+ border-right-style: solid;
+ border-color: transparent $triangle-color transparent transparent;
+ }
+}
+
+/// Creates a menu icon with a set width, height, number of bars, and colors. The mixin uses the height of the icon and the weight of the bars to determine spacing. <div class="docs-example-burger"></div>
+///
+/// @param {Color} $color [$black] - Color to use for the icon.
+/// @param {Color} $color-hover [$dark-gray] - Color to use when the icon is hovered over.
+/// @param {Number} $width [20px] - Width of the icon.
+/// @param {Number} $height [16px] - Height of the icon.
+/// @param {Number} $weight [2px] - Height of individual bars in the icon.
+/// @param {Number} $bars [3] - Number of bars in the icon.
+@mixin hamburger(
+ $color: $black,
+ $color-hover: $dark-gray,
+ $width: 20px,
+ $height: 16px,
+ $weight: 2px,
+ $bars: 3
+) {
+ // box-shadow CSS output
+ $shadow: ();
+ $hover-shadow: ();
+
+ // Spacing between bars is calculated based on the total height of the icon and the weight of each bar
+ $spacing: ($height - ($weight * $bars)) / ($bars - 1);
+
+ @if unit($spacing) == 'px' {
+ $spacing: floor($spacing);
+ }
+
+ @for $i from 2 through $bars {
+ $offset: ($weight + $spacing) * ($i - 1);
+ $shadow: append($shadow, 0 $offset 0 $color, comma);
+ }
+
+ // Icon container
+ position: relative;
+ display: inline-block;
+ vertical-align: middle;
+ width: $width;
+ height: $height;
+ cursor: pointer;
+
+ // Icon bars
+ &::after {
+ position: absolute;
+ top: 0;
+ left: 0;
+
+ display: block;
+ width: 100%;
+ height: $weight;
+
+ background: $color;
+ box-shadow: $shadow;
+
+ content: '';
+ }
+
+ // Hover state
+ @if $color-hover {
+ // Generate CSS
+ @for $i from 2 through $bars {
+ $offset: ($weight + $spacing) * ($i - 1);
+ $hover-shadow: append($hover-shadow, 0 $offset 0 $color-hover, comma);
+ }
+
+ &:hover::after {
+ background: $color-hover;
+ box-shadow: $hover-shadow;
+ }
+ }
+}
+
+/// Adds a downward-facing triangle as a background image to an element. The image is formatted as an SVG, making it easy to change the color. Because Internet Explorer doesn't support encoded SVGs as background images, a PNG fallback is also included.
+/// There are two PNG fallbacks: a black triangle and a white triangle. The one used depends on the lightness of the input color.
+///
+/// @param {Color} $color [$black] - Color to use for the triangle.
+@mixin background-triangle($color: $black) {
+ $rgb: 'rgb%28#{round(red($color))}, #{round(green($color))}, #{round(blue($color))}%29';
+
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='32' height='24' viewBox='0 0 32 24'><polygon points='0,0 32,0 16,24' style='fill: #{$rgb}'></polygon></svg>");
+
+ @media screen and (min-width:0\0) {
+ @if lightness($color) < 60% {
+ // White triangle
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAYAAACbU/80AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIpJREFUeNrEkckNgDAMBBfRkEt0ObRBBdsGXUDgmQfK4XhH2m8czQAAy27R3tsw4Qfe2x8uOO6oYLb6GlOor3GF+swURAOmUJ+RwtEJs9WvTGEYxBXqI1MQAZhCfUQKRzDMVj+TwrAIV6jvSUEkYAr1LSkcyTBb/V+KYfX7xAeusq3sLDtGH3kEGACPWIflNZfhRQAAAABJRU5ErkJggg==');
+ }
+ @else {
+ // Black triangle
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAYAAACbU/80AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMBJREFUeNrEllsOhCAMRVszC9IlzU7KCmVHTJsoMWYMUtpyv9BgbuXQB5ZSdgBYYY4ycgBivk8KYFsQMfMiTTBP4o3nUzCKzOabLJbLy2/g31evGkAginR4/ZegKH5qX3bJCscA3t0x3kgO5tQFyhhFf50xRqFLbyMUNJQzgyjGS/wgCpvKqkRBpuWrE4V9d+1E4dPUXqIg107SQOE/2DRQxMwTDygIInVDET9T3lCoj/6j/VCmGjZOl2lKpZ8AAwDQP7zIimDGFQAAAABJRU5ErkJggg==');
+ }
+ }
+}
+
+/// Applies the micro clearfix hack popularized by Nicolas Gallagher. Include this mixin on a container if its children are all floated, to give the container a proper height.
+/// The clearfix is augmented with specific styles to prevent borders in flexbox environments
+/// @link http://nicolasgallagher.com/micro-clearfix-hack/ Micro Clearfix Hack
+/// @link http://danisadesigner.com/blog/flexbox-clear-fix-pseudo-elements/ Flexbox fix
+@mixin clearfix {
+ &::before,
+ &::after {
+ display: table;
+ content: ' ';
+
+ @if $global-flexbox {
+ flex-basis: 0;
+ order: 1;
+ }
+ }
+
+ &::after {
+ clear: both;
+ }
+}
+
+/// Adds CSS for a "quantity query" selector that automatically sizes elements based on how many there are inside a container.
+/// @link http://alistapart.com/article/quantity-queries-for-css Quantity Queries for CSS
+///
+/// @param {Number} $max - Maximum number of items to detect. The higher this number is, the more CSS that's required to cover each number of items.
+/// @param {Keyword} $elem [li] - Tag to use for sibling selectors.
+@mixin auto-width($max, $elem: li) {
+ @for $i from 2 through $max {
+ &:nth-last-child(#{$i}):first-child,
+ &:nth-last-child(#{$i}):first-child ~ #{$elem} {
+ width: percentage(1 / $i);
+ }
+ }
+}
+
+/// Removes the focus ring around an element when a mouse input is detected.
+@mixin disable-mouse-outline {
+ [data-whatinput='mouse'] & {
+ outline: 0;
+ }
+}
+
+/// Makes an element visually hidden, but still accessible to keyboards and assistive devices.
+/// @link http://snook.ca/archives/html_and_css/hiding-content-for-accessibility Hiding Content for Accessibility
+/// @link http://hugogiraudel.com/2016/10/13/css-hide-and-seek/
+///
+/// @param {Boolean} $enforce - If `true`, use `!important` on applied properties
+@mixin element-invisible(
+ $enforce: true
+) {
+ $important: if($enforce, '!important', null);
+
+ position: absolute #{$important};
+ width: 1px #{$important};
+ height: 1px #{$important};
+ padding: 0 #{$important};
+ overflow: hidden #{$important};
+ clip: rect(0,0,0,0) #{$important};
+ white-space: nowrap #{$important};
+ border: 0 #{$important};
+}
+
+/// Reverses the CSS output created by the `element-invisible()` mixin.
+/// @param {Boolean} $enforce - If `true`, use `!important` on applied properties
+@mixin element-invisible-off(
+ $enforce: true
+) {
+ $important: if($enforce, '!important', null);
+
+ position: static #{$important};
+ width: auto #{$important};
+ height: auto #{$important};
+ overflow: visible #{$important};
+ clip: auto #{$important};
+ white-space: normal #{$important};
+}
+
+/// Vertically centers the element inside of its first non-static parent,
+/// @link http://www.sitepoint.com/centering-with-sass/ Centering With Sass
+@mixin vertical-center {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+/// Horizontally centers the element inside of its first non-static parent,
+/// @link http://www.sitepoint.com/centering-with-sass/ Centering With Sass
+@mixin horizontal-center {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+/// Absolutely centers the element inside of its first non-static parent,
+/// @link http://www.sitepoint.com/centering-with-sass/ Centering With Sass
+@mixin absolute-center {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+/// Iterates through breakpoints defined in `$breakpoint-classes` and prints the CSS inside the mixin at each breakpoint's media query. Use this with the grid, or any other component that has responsive classes.
+///
+/// @param {Boolean} $small [true] - If `false`, the mixin will skip the `small` breakpoint. Use this with components that don't prefix classes with `small-`, only `medium-` and up.
+/// @param {Boolean} $auto-insert-breakpoints [true] - If `false`, the mixin will iterate over breakpoints without doing the media query itself. Useful for more complex media query generation as in the margin grid.
+@mixin -zf-each-breakpoint(
+ $small: true,
+ $auto-insert-breakpoints: true
+) {
+ @include -zf-each-breakpoint-in(auto, -zf-bool($small), -zf-bool($auto-insert-breakpoints)) {
+ @content
+ };
+}
+
+/// Iterates with `@content` through the given list of breakpoints `$breakpoints`.
+///
+/// @access private
+///
+/// @param {Keyword|List} $breakpoints [auto] - Breakpoints to iterates on. It can be a breakpoint name, list of breakpoints or `auto` for all breakpoints.
+/// @param {Boolean|Null} $zero-breakpoint [null] - Whether the zero-breakpoint (often `small`) must be included. If `true`, it will always be added to the list if not already there. If `false`, it will always be removed. Does nothing by default.
+/// @param {Boolean|Keyword} $media-queries [true] - Whether media-queries must be generated. If `for-lists`, only generate media-queries when `$breakpoints` is a list.
+@mixin -zf-each-breakpoint-in(
+ $breakpoints: auto,
+ $zero-breakpoint: null,
+ $media-queries: true
+) {
+ $-list: ();
+ $-breakpoints-is-a-list: true;
+
+ // Retrieve the list of breakpoint(s) to iterate on.
+ @if $breakpoints == auto {
+ $-list: $breakpoint-classes;
+ }
+ @else if type-of($breakpoints) == 'list' {
+ $-list: $breakpoints;
+ }
+ @else if type-of($breakpoints) == 'string' {
+ $-list: ($breakpoints);
+ $-breakpoints-is-a-list: false;
+ }
+ @else {
+ @error 'Wrong syntax for "$breakpoints" in "-zf-each-breakpoint-in()". Got "#{$breakpoints}" (#{type-of($breakpoints)}). Expected a breakpoint name, a list of breakpoints or "auto"';
+ }
+
+ // Add or remove the zero breakpoint according to `$zero-breakpoint`
+ @if $zero-breakpoint == true {
+ $-list: join(($-zf-zero-breakpoint), sl-remove($-list, $-zf-zero-breakpoint));
+ }
+ @else if $zero-breakpoint == false {
+ $-list: sl-remove($-list, $-zf-zero-breakpoint);
+ }
+
+ // Iterate on breakpoint(s)
+ @each $bp in $-list {
+ $old-zf-size: null;
+ @if global-variable-exists(-zf-size) {
+ $old-zf-size: $-zf-size;
+ }
+ $-zf-size: $bp !global;
+
+ @if ($media-queries == true
+ or ($media-queries == 'for-lists' and $-breakpoints-is-a-list)) {
+ @include breakpoint($bp) {
+ @content;
+ }
+ }
+ @else {
+ @content;
+ }
+
+ $-zf-size: $old-zf-size !global;
+ }
+}
+
+/// Generate the `@content` passed to the mixin with a value `$-zf-bp-value` related to a breakpoint, depending on the `$name` parameter:
+/// - For a single value, `$-zf-bp-value` is this value.
+/// - For a breakpoint name, `$-zf-bp-value` is the corresponding breakpoint value in `$map`.
+/// - For "auto", `$-zf-bp-value` is the corresponding breakpoint value in `$map` and is passed to `@content`, which is made responsive for each breakpoint of `$map`.
+/// @param {Number|Array|Keyword} $name [auto] - Single value, breakpoint name, or list of breakpoint names to use. "auto" by default.
+/// @param {Number|Map} $map - Map of breakpoints and values or single value to use.
+@mixin -zf-breakpoint-value(
+ $name: auto,
+ $map: null
+) {
+ @if $name == auto and type-of($map) == 'map' {
+ // "auto"
+ @each $k, $v in $map {
+ @include breakpoint($k) {
+ @include -zf-breakpoint-value($v, $map) {
+ @content;
+ }
+ }
+ }
+ }
+ @else {
+ // breakpoint name
+ @if type-of($name) == 'string' {
+ $bp-value: -zf-get-bp-val($map, $name);
+ @if $bp-value != null {
+ $name: $bp-value;
+ }
+ }
+
+ // breakpoint value
+ $-zf-bp-value: $name !global;
+ @content;
+ }
+}
diff --git a/src/foundation/util/_selector.scss b/src/foundation/util/_selector.scss
new file mode 100644
index 0000000..2c79c04
--- /dev/null
+++ b/src/foundation/util/_selector.scss
@@ -0,0 +1,41 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group functions
+////
+
+/// Generates a selector with every text input type. You can also filter the list to only output a subset of those selectors.
+///
+/// @param {List|Keyword} $types [()] - A list of text input types to use. Leave blank to use all of them.
+/// @param {Keyword} $modifier [''] - A modifier to be applied to each text input type (e.g. a class or a pseudo-class). Leave blank to ignore.
+@function text-inputs($types: (), $modifier: '') {
+ $return: ();
+
+ $all-types:
+ text
+ password
+ date
+ datetime
+ datetime-local
+ month
+ week
+ email
+ number
+ search
+ tel
+ time
+ url
+ color;
+
+ @if not has-value($types) {
+ $types: $all-types;
+ }
+
+ @each $type in $types {
+ $return: append($return, unquote('[type=\'#{$type}\']#{$modifier}'), comma);
+ }
+
+ @return $return;
+}
diff --git a/src/foundation/util/_typography.scss b/src/foundation/util/_typography.scss
new file mode 100644
index 0000000..adff086
--- /dev/null
+++ b/src/foundation/util/_typography.scss
@@ -0,0 +1,26 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group functions
+////
+
+$-zf-font-stack: (
+ 'georgia': (Georgia, "URW Bookman L", serif),
+ 'helvetica': (Helvetica, Arial, "Nimbus Sans L", sans-serif),
+ 'lucida-grande': ("Lucida Grande", "Lucida Sans Unicode", "Bitstream Vera Sans", sans-serif),
+ 'monospace': ("Courier New", Courier, "Nimbus Sans L", monospace),
+ 'system': (-apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif),
+ 'verdana': (Verdana, Geneva, "DejaVu Sans", sans-serif),
+);
+
+/// Return a font stack list from a map. Equivalent to `map-safe-get($name, $-zf-font-stack)`.
+///
+/// @param {String} $stack - Name of the font stack.
+/// @param {Map} $map [$-zf-font-stack] - Map of font stacks to retrieve a list from.
+///
+/// @returns {List} Found font stack.
+@function font-stack($stack, $map: $-zf-font-stack) {
+ @return map-safe-get($map, $stack);
+}
diff --git a/src/foundation/util/_unit.scss b/src/foundation/util/_unit.scss
new file mode 100644
index 0000000..9496226
--- /dev/null
+++ b/src/foundation/util/_unit.scss
@@ -0,0 +1,152 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group functions
+////
+
+$global-font-size: 100% !default;
+
+/// Removes the unit (e.g. px, em, rem) from a value, returning the number only.
+///
+/// @param {Number} $num - Number to strip unit from.
+///
+/// @returns {Number} The same number, sans unit.
+@function strip-unit($num) {
+ @return $num / ($num * 0 + 1);
+}
+
+/// Converts one or more pixel values into matching rem values.
+///
+/// @param {Number|List} $values - One or more values to convert. Be sure to separate them with spaces and not commas. If you need to convert a comma-separated list, wrap the list in parentheses.
+/// @param {Number} $base [null] - The base value to use when calculating the `rem`. If you're using Foundation out of the box, this is 16px. If this parameter is `null`, the function will reference the `$global-font-size` variable as the base.
+///
+/// @returns {List} A list of converted values.
+@function rem-calc($values, $base: null) {
+ $rem-values: ();
+ $count: length($values);
+
+ // If no base is defined, defer to the global font size
+ @if $base == null {
+ $base: $global-font-size;
+ }
+
+ // If the base font size is a %, then multiply it by 16px
+ // This is because 100% font size = 16px in most all browsers
+ @if unit($base) == '%' {
+ $base: ($base / 100%) * 16px;
+ }
+
+ // Using rem as base allows correct scaling
+ @if unit($base) == 'rem' {
+ $base: strip-unit($base) * 16px;
+ }
+
+ @if $count == 1 {
+ @return -zf-to-rem($values, $base);
+ }
+
+ @for $i from 1 through $count {
+ $rem-values: append($rem-values, -zf-to-rem(nth($values, $i), $base));
+ }
+
+ @return $rem-values;
+}
+
+// Converts a unitless, pixel, or rem value to em, for use in breakpoints.
+@function -zf-bp-to-em($value) {
+ // Pixel and unitless values are converted to rems
+ @if unit($value) == 'px' or unitless($value) {
+ $value: rem-calc($value, $base: 16px);
+ }
+
+ // Then the value is converted to ems
+ @return strip-unit($value) * 1em;
+}
+
+/// Converts a pixel value to matching rem value. *Any* value passed, regardless of unit, is assumed to be a pixel value. By default, the base pixel value used to calculate the rem value is taken from the `$global-font-size` variable.
+/// @access private
+///
+/// @param {Number} $value - Pixel value to convert.
+/// @param {Number} $base [null] - Base for pixel conversion.
+///
+/// @returns {Number} A number in rems, calculated based on the given value and the base pixel value. rem values are passed through as is.
+@function -zf-to-rem($value, $base: null) {
+ // Check if the value is a number
+ @if type-of($value) != 'number' {
+ @warn inspect($value) + ' was passed to rem-calc(), which is not a number.';
+ @return $value;
+ }
+
+ // Transform em into rem if someone hands over 'em's
+ @if unit($value) == 'em' {
+ $value: strip-unit($value) * 1rem;
+ }
+
+ // Calculate rem if units for $value is not rem or em
+ @if unit($value) != 'rem' {
+ $value: strip-unit($value) / strip-unit($base) * 1rem;
+ }
+
+ // Turn 0rem into 0
+ @if $value == 0rem {
+ $value: 0;
+ }
+
+ @return $value;
+}
+
+/// Converts a pixel, percentage, rem or em value to a unitless value based on a given font size. Ideal for working out unitless line heights.
+///
+/// @param {Number} $value - Value to convert to a unitless line height
+/// @param {Number} $base - The font size to use to work out the line height - defaults to $global-font-size
+///
+/// @return {Number} - Unitless number
+@function unitless-calc($value, $base: null) {
+
+ // If no base is defined, defer to the global font size
+ @if $base == null {
+ $base: $global-font-size;
+ }
+
+ // First, lets convert our $base to pixels
+
+ // If the base font size is a %, then multiply it by 16px
+ @if unit($base) == '%' {
+ $base: ($base / 100%) * 16px;
+ }
+
+ @if unit($base) == 'rem' {
+ $base: strip-unit($base) * 16px;
+ }
+
+ @if unit($base) == 'em' {
+ $base: strip-unit($base) * 16px;
+ }
+
+ // Now let's convert our value to pixels too
+ @if unit($value) == '%' {
+ $value: ($value / 100%) * $base;
+ }
+
+ @if unit($value) == 'rem' {
+ $value: strip-unit($value) * $base;
+ }
+
+ @if unit($value) == 'em' {
+ $value: strip-unit($value) * $base;
+ }
+
+ // 'px'
+ @if unit($value) == 'px' {
+ @return strip-unit($value) / strip-unit($base);
+ }
+
+ // assume that line-heights greater than 10 are meant to be absolute in 'px'
+ @if unitless($value) and ($value > 10) {
+ @return $value / strip-unit($base);
+ }
+
+ @return $value;
+}
diff --git a/src/foundation/util/_util.scss b/src/foundation/util/_util.scss
new file mode 100644
index 0000000..ddcb59e
--- /dev/null
+++ b/src/foundation/util/_util.scss
@@ -0,0 +1,14 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+@import 'math';
+@import 'unit';
+@import 'value';
+@import 'direction';
+@import 'color';
+@import 'selector';
+@import 'flex';
+@import 'breakpoint';
+@import 'mixins';
+@import 'typography';
diff --git a/src/foundation/util/_value.scss b/src/foundation/util/_value.scss
new file mode 100644
index 0000000..03053fd
--- /dev/null
+++ b/src/foundation/util/_value.scss
@@ -0,0 +1,200 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group functions
+////
+
+/// Determine if a value is not falsey, in CSS terms. Falsey values are `null`, `none`, `0` with any unit, or an empty list.
+///
+/// @param {Mixed} $val - Value to check.
+///
+/// @returns {Boolean} `true` if `$val` is not falsey.
+@function has-value($val) {
+ @if $val == null or $val == none {
+ @return false;
+ }
+ @if type-of($val) == 'number' and strip-unit($val) == 0 {
+ @return false;
+ }
+ @if type-of($val) == 'list' and length($val) == 0 {
+ @return false;
+ }
+ @return true;
+}
+
+/// Determine a top/right/bottom/right value on a padding, margin, etc. property, no matter how many values were passed in. Use this function if you need to know the specific side of a value, but don't know if the value is using a shorthand format.
+///
+/// @param {List|Number} $val - Value to analyze. Should be a shorthand sizing property, e.g. "1em 2em 1em"
+/// @param {Keyword} $side - Side to return. Should be `top`, `right`, `bottom`, or `left`.
+///
+/// @returns {Number} A single value based on `$val` and `$side`.
+@function get-side($val, $side) {
+ $length: length($val);
+
+ @if $length == 1 {
+ @return $val;
+ }
+ @if $length == 2 {
+ @return map-get((
+ top: nth($val, 1),
+ bottom: nth($val, 1),
+ left: nth($val, 2),
+ right: nth($val, 2),
+ ), $side);
+ }
+ @if $length == 3 {
+ @return map-get((
+ top: nth($val, 1),
+ left: nth($val, 2),
+ right: nth($val, 2),
+ bottom: nth($val, 3),
+ ), $side);
+ }
+ @if $length == 4 {
+ @return map-get((
+ top: nth($val, 1),
+ right: nth($val, 2),
+ bottom: nth($val, 3),
+ left: nth($val, 4),
+ ), $side);
+ }
+}
+
+/// Given border $val, find a specific element of the border, which is $elem. The possible values for $elem are width, style, and color.
+///
+/// @param {List} $val - Border value to find a value in.
+/// @param {Keyword} $elem - Border component to extract.
+///
+/// @returns {Mixed} If the value exists, returns the value. If the value is not in the border definition, the function will return a 0px width, solid style, or black border.
+@function get-border-value($val, $elem) {
+ // Find the width, style, or color and return it
+ @each $v in $val {
+ $type: type-of($v);
+ @if $elem == width and $type == 'number' {
+ @return $v;
+ }
+ @if $elem == style and $type == 'string' {
+ @return $v;
+ }
+ @if $elem == color and $type == 'color' {
+ @return $v;
+ }
+ }
+
+ // Defaults
+ $defaults: (
+ width: 0,
+ style: solid,
+ color: #000,
+ );
+
+ @return map-get($defaults, $elem);
+}
+
+/// Finds a value in a nested map.
+/// @link https://css-tricks.com/snippets/sass/deep-getset-maps/ Deep Get/Set in Maps
+///
+/// @param {Map} $map - Map to pull a value from.
+/// @param {String} $keys... - Keys to use when looking for a value.
+/// @returns {Mixed} The value found in the map.
+@function map-deep-get($map, $keys...) {
+ @each $key in $keys {
+ $map: map-get($map, $key);
+ }
+ @return $map;
+}
+
+/// Casts a map into a list.
+/// @link http://hugogiraudel.com/2014/04/28/casting-map-into-list/
+///
+/// @param {Map} $map - Map to pull a value from.
+///
+/// @returns {List} Depending on the flag, returns either $keys or $values or both.
+@function map-to-list($map, $keep: 'both') {
+ $keep: if(index('keys' 'values', $keep), $keep, 'both');
+
+ @if type-of($map) == 'map' {
+ $keys: ();
+ $values: ();
+
+ @each $key, $val in $map {
+ $keys: append($keys, $key);
+ $values: append($values, $val);
+ }
+
+ @if $keep == 'keys' {
+ @return $keys;
+ }
+ @else if $keep == 'values' {
+ @return $values;
+ }
+ @else {
+ @return zip($keys, $values);
+ }
+ }
+
+ @return if(type-of($map) != 'list', ($value,), $map);
+
+}
+
+/// Return a join of the two given strings `$str1` and `$str2`.
+/// If the two strings are not empty, they are separated by `$delimiter`.
+///
+/// @param {String} $str1 [null] - First string to join.
+/// @param {String} $str1 [null] - Second string to join.
+/// @param {String} $delimiter [null] - Delimieter between `$str1` and `$str2`.
+///
+/// @returns {String} Join of `$str1`, `$delimiter` and `$str2`.
+@function zf-str-join(
+ $str1: null,
+ $str2: null,
+ $delimiter: null
+) {
+ $ret: '';
+
+ @if $str1 and str-length($str1) > 0 {
+ $ret: $ret + $str1;
+
+ @if $delimiter and str-length($delimiter) > 0 and $str2 and str-length($str2) > 0 {
+ $ret: $ret + $delimiter;
+ }
+ }
+ @if $str2 and str-length($str2) > 0 {
+ $ret: $ret + $str2;
+ }
+
+ @return $ret;
+}
+
+/// Safely return a value from a map.
+///
+/// @param {Map} $map - Map to retrieve a value from.
+/// @param {String} $key - Name of the map key.
+///
+/// @returns {List} Found value.
+@function map-safe-get($map, $key) {
+ @if (type-of($map) == 'map' or (type-of($map) == 'list' and length($map) == 0)) {
+ @if (map-has-key($map, $key)) {
+ @return map-get($map, $key);
+ }
+ @else {
+ @error 'Key: `#{$key}` is not available in `#{$map}`';
+ }
+ }
+ @else {
+ @error '`#{$map}` is not a valid map';
+ }
+}
+
+/// Convert the given `$val` to a Boolean. Empty values are considered as false.
+////
+/// @access private
+///
+/// @param {*} $val - Value to convert.
+///
+/// @returns {Boolean} Converted Boolean value.
+@function -zf-bool($val) {
+ @return $val != false and has-value($val);
+}
diff --git a/src/foundation/vendor/normalize.scss b/src/foundation/vendor/normalize.scss
new file mode 100644
index 0000000..1023303
--- /dev/null
+++ b/src/foundation/vendor/normalize.scss
@@ -0,0 +1,281 @@
+@mixin foundation-normalize() {
+ /*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */
+
+ // Document
+ // ==========================================================================
+
+ // 1. Correct the line height in all browsers.
+ // 2. Prevent adjustments of font size after orientation changes in iOS.
+
+ html {
+ line-height: 1.15; // 1
+ -webkit-text-size-adjust: 100%; // 2
+ }
+
+ // Sections
+ // ==========================================================================
+
+ // Remove the margin in all browsers.
+
+ body {
+ margin: 0;
+ }
+
+ // Correct the font size and margin on `h1` elements within `section` and
+ // `article` contexts in Chrome, Firefox, and Safari.
+
+ h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+ }
+
+ // Grouping content
+ // ==========================================================================
+
+ // 1. Add the correct box sizing in Firefox.
+ // 2. Show the overflow in Edge and IE.
+
+ hr {
+ box-sizing: content-box; // 1
+ height: 0; // 1
+ overflow: visible; // 2
+ }
+
+ // 1. Correct the inheritance and scaling of font size in all browsers.
+ // 2. Correct the odd `em` font sizing in all browsers.
+
+ pre {
+ font-family: monospace, monospace; // 1
+ font-size: 1em; // 2
+ }
+
+ // Text-level semantics
+ // ==========================================================================
+
+ // Remove the gray background on active links in IE 10.
+
+ a {
+ background-color: transparent;
+ }
+
+ // 1. Remove the bottom border in Chrome 57-
+ // 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+
+ abbr[title] {
+ border-bottom: none; // 1
+ text-decoration: underline; // 2
+ text-decoration: underline dotted; // 2
+ }
+
+ // Add the correct font weight in Chrome, Edge, and Safari.
+
+ b,
+ strong {
+ font-weight: bolder;
+ }
+
+ // 1. Correct the inheritance and scaling of font size in all browsers.
+ // 2. Correct the odd `em` font sizing in all browsers.
+
+ code,
+ kbd,
+ samp {
+ font-family: monospace, monospace; // 1
+ font-size: 1em; // 2
+ }
+
+ // Add the correct font size in all browsers.
+
+ small {
+ font-size: 80%;
+ }
+
+ // Prevent `sub` and `sup` elements from affecting the line height in
+ // all browsers.
+
+ sub,
+ sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+ }
+
+ sub {
+ bottom: -0.25em;
+ }
+
+ sup {
+ top: -0.5em;
+ }
+
+ // Embedded content
+ // ==========================================================================
+
+ // Remove the border on images inside links in IE 10.
+
+ img {
+ border-style: none;
+ }
+
+ // Forms
+ // ==========================================================================
+
+ // 1. Change the font styles in all browsers.
+ // 2. Remove the margin in Firefox and Safari.
+
+ button,
+ input,
+ optgroup,
+ select,
+ textarea {
+ font-family: inherit; // 1
+ font-size: 100%; // 1
+ line-height: 1.15; // 1
+ margin: 0; // 2
+ }
+
+ // Show the overflow in IE.
+ // 1. Show the overflow in Edge.
+
+ button,
+ input { // 1
+ overflow: visible;
+ }
+
+ // Remove the inheritance of text transform in Edge, Firefox, and IE.
+ // 1. Remove the inheritance of text transform in Firefox.
+
+ button,
+ select { // 1
+ text-transform: none;
+ }
+
+ // Correct the inability to style clickable types in iOS and Safari.
+
+ button,
+ [type="button"],
+ [type="reset"],
+ [type="submit"] {
+ -webkit-appearance: button;
+ }
+
+ // Remove the inner border and padding in Firefox.
+
+ button::-moz-focus-inner,
+ [type="button"]::-moz-focus-inner,
+ [type="reset"]::-moz-focus-inner,
+ [type="submit"]::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+ }
+
+ // Restore the focus styles unset by the previous rule.
+
+ button:-moz-focusring,
+ [type="button"]:-moz-focusring,
+ [type="reset"]:-moz-focusring,
+ [type="submit"]:-moz-focusring {
+ outline: 1px dotted ButtonText;
+ }
+
+ // Correct the padding in Firefox.
+
+ fieldset {
+ padding: 0.35em 0.75em 0.625em;
+ }
+
+ // 1. Correct the text wrapping in Edge and IE.
+ // 2. Correct the color inheritance from `fieldset` elements in IE.
+ // 3. Remove the padding so developers are not caught out when they zero out
+ // `fieldset` elements in all browsers.
+
+ legend {
+ box-sizing: border-box; // 1
+ color: inherit; // 2
+ display: table; // 1
+ max-width: 100%; // 1
+ padding: 0; // 3
+ white-space: normal; // 1
+ }
+
+ // Add the correct vertical alignment in Chrome, Firefox, and Opera.
+
+ progress {
+ vertical-align: baseline;
+ }
+
+ // Remove the default vertical scrollbar in IE 10+.
+
+ textarea {
+ overflow: auto;
+ }
+
+ // 1. Add the correct box sizing in IE 10.
+ // 2. Remove the padding in IE 10.
+
+ [type="checkbox"],
+ [type="radio"] {
+ box-sizing: border-box; // 1
+ padding: 0; // 2
+ }
+
+ // Correct the cursor style of increment and decrement buttons in Chrome.
+
+ [type="number"]::-webkit-inner-spin-button,
+ [type="number"]::-webkit-outer-spin-button {
+ height: auto;
+ }
+
+ // 1. Correct the odd appearance in Chrome and Safari.
+ // 2. Correct the outline style in Safari.
+
+ [type="search"] {
+ -webkit-appearance: textfield; // 1
+ outline-offset: -2px; // 2
+ }
+
+ // Remove the inner padding in Chrome and Safari on macOS.
+
+ [type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+ }
+
+ // 1. Correct the inability to style clickable types in iOS and Safari.
+ // 2. Change font properties to `inherit` in Safari.
+
+ ::-webkit-file-upload-button {
+ -webkit-appearance: button; // 1
+ font: inherit; // 2
+ }
+
+ // Interactive
+ // ==========================================================================
+
+ // Add the correct display in Edge, IE 10+, and Firefox.
+
+ details {
+ display: block;
+ }
+
+ // Add the correct display in all browsers.
+
+ summary {
+ display: list-item;
+ }
+
+ // Misc
+ // ==========================================================================
+
+ // Add the correct display in IE 10+.
+
+ template {
+ display: none;
+ }
+
+ // Add the correct display in IE 10.
+
+ [hidden] {
+ display: none;
+ }
+}
diff --git a/src/foundation/xy-grid/_cell.scss b/src/foundation/xy-grid/_cell.scss
new file mode 100644
index 0000000..b851b6a
--- /dev/null
+++ b/src/foundation/xy-grid/_cell.scss
@@ -0,0 +1,272 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group xy-grid
+////
+
+/// Returns the appropriate CSS flex value for a cell base.
+///
+/// @param {Keyword} $size [full] - The size of your cell. Accepts `full`, `auto`, `shrink`, `grow`, or any other value representing a cell size (it will be treated as `shrink`).
+///
+/// @returns {List} The cell flex property value.
+@function xy-cell-base($size: full) {
+ @if ($size == 'auto') {
+ @return 1 1 0px;
+ }
+ @else if ($size == 'grow') {
+ @return 1 0 auto;
+ }
+ @else if ($size == 'shrink' or $size == 'full' or zf-is-fraction($size, $allow-no-denominator: true)) {
+ @return 0 0 auto;
+ }
+ @return null;
+}
+
+/// Calculate the size of a cell gutters.
+///
+/// @param {Number|Map} $gutters [$grid-margin-gutters] - Map or single value for gutters.
+/// @param {String} $breakpoint [null] - The name of the breakpoint size in your gutters map to get the size from. If `auto`, returns the responsive gutters map `$gutters`. If using with the `breakpoint()` mixin this will be set automatically unless manually entered.
+///
+/// @returns {Number|Map} The cell gutter size or the responsive gutters map.
+@function xy-cell-gutters(
+ $gutters: $grid-margin-gutters,
+ $breakpoint: null
+) {
+ // For `auto`, returns the responsive map `$gutters`.
+ @if ($breakpoint == 'auto') {
+ @return $gutters;
+ }
+
+ // Use the contextual breakpoint by default.
+ $breakpoint: -zf-current-breakpoint($breakpoint);
+
+ @if ($breakpoint) {
+ @return -zf-get-bp-val($gutters, $breakpoint);
+ }
+ @else {
+ @return -zf-get-bp-val($gutters, $-zf-zero-breakpoint) or 0;
+ }
+}
+
+/// Returns the percentage size of a cell.
+///
+/// @param {Number|List} $size [$grid-columns] - Size to make the cell. You can pass a value in multiple formats, such as `6`, `50%`, `1 of 2` or `1/3`.
+///
+/// @returns {Number} Size of the cell (in percent).
+@function xy-cell-size(
+ $size: $grid-columns
+) {
+ @return fraction-to-percentage($size, $denominator: $grid-columns);
+}
+
+/// Returns the appropriate CSS value for a cell size.
+///
+/// Gutters-related arguments are required for cells with margin gutters (by default) as the gutter is included in the width.
+///
+/// @param {Keyword|Number} $size [full] - The size of your cell. Can be `full`, `auto`, `shrink` or any fraction like `6`, `50%`, `1 of 2` or `1/2`.
+/// @param {Number|Map} $gutters [$grid-margin-gutters] - Map or single value for gutters.
+/// @param {Keyword} $gutter-type [margin] - Type of gutter to output. Accepts `margin`, `padding` or `none`.
+/// @param {String} $breakpoint [null] - The name of the breakpoint size in your gutters map to get the size from. If `auto`, returns a map of sizes adapted to responsive gutters. If using with the `breakpoint()` mixin this will be set automatically unless manually entered.
+///
+/// @returns {Number|String|Map} The cell sizing property value, or a responsive map of them.
+@function xy-cell-size-css(
+ $size: full,
+ $gutters: $grid-margin-gutters,
+ $gutter-type: margin,
+ $breakpoint: null
+) {
+ $margin-gutter: 0;
+
+ @if ($size == 'auto' or $size == 'shrink') {
+ @return auto;
+ }
+
+ // For cells with margin gutters, the gutter is included in the width.
+ @if ($gutter-type == 'margin') {
+ $margin-gutter: xy-cell-gutters($gutters, $breakpoint);
+ @if ($margin-gutter == null) {
+ @error 'xy-cell-size: no gutters were found in `$gutters` for "$breakpoint: #{$breakpoint}"';
+ }
+ }
+
+ // Calculate the cell size (number)
+ $size-raw: if($size == 'full', 100%, xy-cell-size($size));
+
+ // Calculate the cell CSS size including gutters (string)
+ // If the cell has responsive margin gutters, return a responsive map of sizes.
+ @if type-of($margin-gutter) == 'map' {
+ $responsive-css-sizes: ();
+
+ @each $bp, $mg in $margin-gutter {
+ $size-css: if($mg == 0, $size-raw, calc(#{$size-raw} - #{rem-calc($mg)}));
+ $responsive-css-sizes: map-merge($responsive-css-sizes, ($bp: $size-css));
+ }
+
+ @return $responsive-css-sizes;
+ }
+ // Otherwise, return a single CSS size.
+ @else {
+ $css-size: if($margin-gutter == 0, $size-raw, calc(#{$size-raw} - #{rem-calc($margin-gutter)}));
+ @return $css-size;
+ }
+}
+
+/// Sets base flex properties for cells.
+///
+/// @param {Keyword} $size [full] - The size of your cell. Accepts `full`, `auto`, `shrink`, `grow`, or any other value representing a cell size (it will be treated as `shrink`).
+@mixin xy-cell-base($size: full) {
+ $base: xy-cell-base($size);
+
+ flex: #{$base};
+
+ // Set base styles for "full" only
+ @if($size == 'full') {
+ min-height: 0px;
+ min-width: 0px;
+ }
+}
+
+/// Resets a cells width (or height if vertical is true) as well as strips its gutters.
+///
+/// @param {Boolean} $vertical [false] - Set to true to output vertical (height) styles rather than widths.
+@mixin xy-cell-reset($vertical: true) {
+ $direction: if($vertical == true, height, width);
+ #{$direction}: auto;
+ max-#{$direction}: none;
+}
+
+/// Sets sizing properties for cells.
+///
+/// Gutters-related arguments are required for cells with margin gutters (by default) as the gutter is included in the width.
+///
+/// @param {Keyword|Number} $size [full] - The size of your cell. Can be `full` (100% width), `auto` (use all available space), `shrink` (use only the required space) or any fraction (`6`, `50%`, `1 of 2` or `1/2`...).
+/// @param {Number|Map} $gutters [$grid-margin-gutters] - Map or single value for gutters.
+/// @param {Keyword} $gutter-type [margin] - Type of gutter to output. Accepts `margin`, `padding` or `none`.
+/// @param {String} $breakpoint [null] - The name of the breakpoint size in your gutters map to get the size from. If `auto`, generates sizes adapted for responsive gutters. If using with the `breakpoint()` mixin this will be set automatically unless manually entered.
+/// @param {Boolean} $vertical [false] - Set to true to output vertical (height) styles rather than widths.
+@mixin xy-cell-size(
+ $size: full,
+ $gutters: $grid-margin-gutters,
+ $gutter-type: margin,
+ $breakpoint: null,
+ $vertical: false
+) {
+ $sizes: xy-cell-size-css($size, $gutters, $gutter-type, $breakpoint);
+ $direction: if($vertical == true, height, width);
+
+ @if (type-of($sizes) == 'map') {
+ @include -zf-breakpoint-value(auto, $sizes) {
+ #{$direction}: $-zf-bp-value;
+ }
+ }
+ @else {
+ #{$direction}: $sizes;
+ }
+}
+
+/// Sets gutters properties for cells.
+///
+/// @param {Number|Map} $gutters [$grid-margin-gutters] - Map or single value for gutters.
+/// @param {Keyword} $gutter-type [margin] - Type of gutter to output. Accepts `margin`, `padding` or `none`.
+/// @param {List} $gutter-position [null] - The position to apply gutters to. Accepts `top`, `bottom`, `left`, `right` in any combination. By default `right left` for horizontal cells and `top bottom` for vertical cells.
+/// @param {String} $breakpoint [null] - The name of the breakpoint size in your gutters map to get the size from. If `auto`, generates responsive gutters. If using with the `breakpoint()` mixin this will be set automatically unless manually entered.
+/// @param {Boolean} $vertical [false] - Direction of the gutters to output. See `$gutter-position`.
+@mixin xy-cell-gutters(
+ $gutters: $grid-margin-gutters,
+ $gutter-type: margin,
+ $gutter-position: null,
+ $breakpoint: null,
+ $vertical: false
+) {
+ // Get the default gutter position according to cell direction
+ @if($gutter-position == null) {
+ $gutter-position: if($vertical == true, top bottom, left right);
+ }
+
+ // Get the gutter width for this breakpoint
+ $gutter-width: xy-cell-gutters($gutters, $breakpoint);
+ @if ($gutter-width == null) {
+ @error 'xy-cell-gutters: no gutters were found in `$gutters` for "$breakpoint: #{$breakpoint}"';
+ }
+
+ @if ($gutter-type and $gutter-type != none) {
+ @include xy-gutters($gutter-width, $gutter-type, $gutter-position);
+ }
+}
+
+/// Creates a cell for your grid.
+///
+/// @param {Keyword|Number} $size [full] - The size of your cell. Can be `full` (100% width), `auto` (use all available space), `shrink` (use only the required space) or any fraction (`6`, `50%`, `1 of 2` or `1/2`...).
+/// @param {Boolean} $gutter-output [null] - [DEPRECATED] Whether or not to output gutters.
+/// @param {Number|Map} $gutters [$grid-margin-gutters] - Map or single value for gutters.
+/// @param {Keyword} $gutter-type [margin] - Type of gutter to output. Accepts `margin`, `padding` or `none`.
+/// @param {List} $gutter-position [null] - The position to apply gutters to. Accepts `top`, `bottom`, `left`, `right` in any combination. By default `right left` for horizontal cells and `top bottom` for vertical cells.
+/// @param {String} $breakpoint [null] - The name of the breakpoint size in your gutters map to get the size from. If `auto`, generates responsive gutters. If using with the `breakpoint()` mixin this will be set automatically unless manually entered.
+/// @param {Boolean} $vertical [false] - Set to true to output vertical (height) styles rather than widths.
+/// @param {List} $output [(base size gutters)] - Cell parts to output. You will need to generate others parts of the cell seperately, it may not work properly otherwise.
+@mixin xy-cell(
+ $size: full,
+ $gutter-output: null,
+ $gutters: $grid-margin-gutters,
+ $gutter-type: margin,
+ $gutter-position: null,
+ $breakpoint: null,
+ $vertical: false,
+ $output: (base size gutters)
+) {
+ // Default for $gutter-output
+ @if ($gutter-output != null) {
+ @warn 'xy-cell: $gutter-output is deprecated and will be removed. See migration notes at https://git.io/foundation-6-6-0';
+ @if ($gutter-output == false) {
+ $output: sl-remove($output, gutters);
+ }
+ }
+
+ @if (index($output, base)) {
+ @include xy-cell-base($size);
+ }
+ @if (index($output, size)) {
+ @include xy-cell-size($size, $gutters, $gutter-type, $breakpoint, $vertical);
+ }
+ @if (index($output, gutters)) {
+ @include xy-cell-gutters($gutters, $gutter-type, $gutter-position, $breakpoint, $vertical);
+ }
+}
+
+/// Creates a single breakpoint sized grid. Used to generate our grid classes.
+///
+/// `xy-cell-static()` is deprecated and will be removed.
+/// Use `xy-cell()` instead with `$output: (size gutters)` to not generate the cell base.
+/// See migration notes at https://git.io/foundation-6-6-0
+///
+/// @deprecated v6.6.0
+///
+/// @param {Keyword|Number} $size [full] - The size of your cell. Can be `full` (100% width), `auto` (use all available space), `shrink` (use only the required space) or any fraction (`6`, `50%`, `1 of 2` or `1/2`...).
+/// @param {Boolean} $gutter-output [true] - Whether or not to output gutters. Always `true` for margin gutters.
+/// @param {Number|Map} $gutters [$grid-margin-gutters] - Map or single value for gutters.
+/// @param {Keyword} $gutter-type [margin] - Map or single value for gutters.
+/// @param {String} $breakpoint [null] - The name of the breakpoint size in your gutters map to get the size from. If using with the `breakpoint()` mixin this will be set automatically unless manually entered.
+/// @param {Boolean} $vertical [false] - Set to true to output vertical (height) styles rather than widths.
+@mixin xy-cell-static(
+ $size: full,
+ $gutter-output: true,
+ $gutters: $grid-margin-gutters,
+ $gutter-type: margin,
+ $breakpoint: $-zf-zero-breakpoint,
+ $vertical: false
+) {
+ @warn 'xy-cell-static() mixin is deprecated and will be removed. Use "xy-cell()" instead. See migration notes at https://git.io/foundation-6-6-0';
+
+ $gutter: -zf-get-bp-val($gutters, $breakpoint);
+ $gutter-position: if($vertical == true, top bottom, left right);
+
+ $-gutter-output: if($gutter-type == 'margin', true, $gutter-output);
+ $-gutter-margin: if($gutter-type == 'margin', $gutter, 0);
+
+ @include -xy-cell-properties($size, $-gutter-margin, $vertical);
+ @if ($-gutter-output) {
+ @include xy-gutters($gutter, $gutter-type, $gutter-position);
+ }
+}
diff --git a/src/foundation/xy-grid/_classes.scss b/src/foundation/xy-grid/_classes.scss
new file mode 100644
index 0000000..11d176e
--- /dev/null
+++ b/src/foundation/xy-grid/_classes.scss
@@ -0,0 +1,493 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group xy-grid
+////
+
+// Margin Grid classes
+@mixin xy-base-grid-classes {
+
+ // Grid Container
+ .grid-container {
+ @include xy-grid-container;
+
+ &.fluid {
+ @include xy-grid-container(100%);
+ }
+
+ &.full {
+ @include xy-grid-container(100%, 0);
+ }
+ }
+
+ // Base grid styles
+ .grid-x {
+ @include xy-grid;
+ }
+
+ .cell {
+ @include xy-cell(full, $gutter-type: none);
+
+ &.auto {
+ @include xy-cell-base(auto);
+ }
+
+ &.shrink {
+ @include xy-cell-base(shrink);
+ }
+
+ }
+ .grid-x {
+ > .auto {
+ @include xy-cell-size(auto, $gutter-type: none);
+ }
+
+ > .shrink {
+ @include xy-cell-size(shrink, $gutter-type: none);
+ }
+ }
+
+ // Auto width
+ @include -zf-each-breakpoint() {
+ // This is a bit of a hack/workaround, see these issues & PRs for the backstory:
+ // https://github.com/zurb/foundation-sites/issues/10244
+ // https://github.com/zurb/foundation-sites/pull/10222 and
+ // https://github.com/zurb/foundation-sites/pull/10164
+ .grid-x {
+ $str: "> .#{$-zf-size}-shrink, > .#{$-zf-size}-full";
+ @for $i from 1 through $grid-columns {
+ $str: $str + ", > .#{$-zf-size}-#{$i}"
+ }
+ #{$str} {
+ flex-basis: auto;
+ }
+ }
+ }
+
+ @include -zf-each-breakpoint() {
+ // Responsive "auto" modifier
+ @if not($-zf-size == $-zf-zero-breakpoint) {
+ .grid-x > .#{$-zf-size}-auto {
+ @include xy-cell(auto, $gutter-type: none);
+ }
+ }
+
+ %-xy-cell-base-shrink-horizontal-#{$-zf-size} {
+ @include xy-cell-base(shrink);
+ }
+
+ // Responsive "shrink" modifier
+ @if not($-zf-size == $-zf-zero-breakpoint) {
+ .grid-x > .#{$-zf-size}-shrink {
+ @extend %-xy-cell-base-shrink-horizontal-#{$-zf-size};
+ @include xy-cell-size(shrink, $gutter-type: none);
+ }
+ }
+
+ // Responsive width modifiers
+ @for $i from 1 through $grid-columns {
+ // Sizing (percentage)
+ .grid-x > .#{$-zf-size}-#{$i} {
+ @extend %-xy-cell-base-shrink-horizontal-#{$-zf-size};
+ @include xy-cell-size($i, $gutter-type: none);
+ }
+ }
+ }
+
+ // Reset width when using `.grid-margin-x` not on `.grid-x`
+ .grid-margin-x:not(.grid-x) > .cell {
+ width: auto;
+ }
+
+ // Reset height when using `.grid-margin-y` not on `.grid-y`
+ .grid-margin-y:not(.grid-y) > .cell {
+ height: auto;
+ }
+}
+
+@mixin -xy-breakpoint-cell-classes($class-breakpoint, $gutter-breakpoint, $vertical) {
+ $prefix: if($class-breakpoint == $-zf-zero-breakpoint, '', '#{$class-breakpoint}-');
+ > .#{$prefix}auto {
+ @include xy-cell-size(auto, $vertical: $vertical);
+ }
+
+ > .#{$prefix}shrink {
+ @include xy-cell-size(shrink, $vertical: $vertical);
+ }
+
+ @for $i from 1 through $grid-columns {
+ // Sizing (percentage)
+ $classname: if($vertical, '.#{$class-breakpoint}-#{$i}', '.#{$class-breakpoint}-#{$i}');
+
+ > #{$classname} {
+ @include xy-cell-size($i, $vertical: $vertical);
+ }
+ }
+}
+
+// Margin Grid classes
+@mixin xy-margin-grid-classes(
+ $gutter-position: left right,
+ $vertical: false,
+ $wrapping-selector: '.grid-margin-x'
+){
+ #{$wrapping-selector} {
+ @include xy-gutters($negative: true, $gutter-position: $gutter-position);
+
+ // Base cell styles
+ > .cell {
+ @include xy-cell($vertical: $vertical, $output: (size gutters));
+ }
+
+ // base styles need to all be before the auto and shrink styles
+ @include -zf-each-breakpoint() {
+ @if(type-of($grid-margin-gutters) == 'map' and map-has-key($grid-margin-gutters, $-zf-size) and $-zf-size != $-zf-zero-breakpoint) {
+ > .cell {
+ @include xy-cell($vertical: $vertical, $output: (size gutters));
+ }
+ }
+ }
+
+ @include -zf-each-breakpoint() {
+
+ // This is purely for responsive gutters - the margin grid has to go back and adjust widths (or heights)
+ // for all prior breakpoints.
+ // As their gutter is defined with their width/height, even breakpoint without a new margin must be
+ // generated to not having their width/height overrided by re-adjusted smaller breakpoints.
+ @if(type-of($grid-margin-gutters) == 'map' and map-has-key($grid-margin-gutters, $-zf-size)) {
+ @each $bp in -zf-breakpoints-less-than($-zf-size) {
+ @include -xy-breakpoint-cell-classes($bp, $-zf-size, $vertical);
+ }
+ }
+
+ @include -xy-breakpoint-cell-classes($-zf-size, $-zf-size, $vertical);
+ }
+ }
+}
+
+// Padding Grid classes
+@mixin xy-padding-grid-classes {
+ .grid-padding-x {
+
+ // Negative margin for nested grids
+ .grid-padding-x {
+ @include xy-gutters($negative: true);
+ }
+
+ // Negative margin for grids within `grid-container/grid-container.fluid`
+ // This allows margin and padding grids to line up with eachother
+ .grid-container:not(.full) > & {
+ @include xy-gutters($negative: true);
+ }
+
+ // Base cell styles
+ > .cell {
+ @include xy-gutters($gutters: $grid-padding-gutters, $gutter-type: padding);
+ }
+ }
+}
+
+// Block Grid classes
+@mixin xy-block-grid-classes($margin-grid: true, $padding-grid: true) {
+ @if $padding-grid {
+ @include -zf-each-breakpoint {
+ @for $i from 1 through $xy-block-grid-max {
+ .#{$-zf-size}-up-#{$i} {
+ @include xy-grid-layout($n: $i, $selector: '.cell', $gutter-type: padding, $output: (size));
+ }
+ }
+ }
+ }
+
+ @if $margin-grid {
+ @include -zf-each-breakpoint {
+ @for $i from 1 through $xy-block-grid-max {
+ // This is purely for responsive gutters - the margin grid has to go back and adjust widths (or heights)
+ // for prior breakpoints based on the responsive gutter.
+ @if(type-of($grid-margin-gutters) == 'map' and map-has-key($grid-margin-gutters, $-zf-size)) {
+ @each $bp in -zf-breakpoints-less-than($-zf-size) {
+ @if(map-has-key($grid-margin-gutters, $bp)) {
+ .grid-margin-x.#{$bp}-up-#{$i} {
+ @include xy-grid-layout($n: $i, $selector: '.cell', $gutter-type: margin, $output: (size));
+ }
+ }
+ }
+ }
+ }
+ @for $i from 1 through $xy-block-grid-max {
+ .grid-margin-x.#{$-zf-size}-up-#{$i} {
+ @include xy-grid-layout($n: $i, $selector: '.cell', $gutter-type: margin, $output: (size));
+ }
+ }
+ }
+ }
+}
+
+// Collapse classes
+@mixin xy-collapse-grid-classes($margin-grid: true, $padding-grid: true) {
+ @each $bp in $breakpoint-classes {
+ @if $margin-grid {
+ .#{$bp}-margin-collapse {
+ @include xy-grid-collapse($gutter-type: margin, $min-breakpoint: $bp);
+ }
+ }
+
+ @if $padding-grid {
+ .#{$bp}-padding-collapse {
+ @include xy-grid-collapse($gutter-type: padding, $min-breakpoint: $bp);
+ }
+ }
+ }
+}
+
+// Offset classes
+@mixin xy-offset-cell-classes {
+ @include -zf-each-breakpoint {
+ @for $i from 1 through $grid-columns {
+ // Offsets
+ $o: $i - 1;
+
+ .#{$-zf-size}-offset-#{$o} {
+ @include xy-cell-offset($o, $gutters: $grid-padding-gutters, $gutter-type: padding);
+ }
+
+ .grid-margin-x > .#{$-zf-size}-offset-#{$o} {
+ @include xy-cell-offset($o);
+ }
+ }
+ }
+}
+
+// Vertical Grid classes
+@mixin xy-vertical-grid-classes(
+ $margin-grid: true,
+ $padding-grid: true
+) {
+
+ @include -zf-each-breakpoint() {
+ @if not($-zf-size == $-zf-zero-breakpoint) {
+ }
+ }
+
+ .grid-y {
+ @include xy-grid(vertical, false);
+
+
+ > .cell {
+ @include xy-cell-reset();
+ }
+
+ > .auto {
+ @include xy-cell-size(auto, $gutter-type: none, $vertical: true);
+ }
+
+ > .shrink {
+ @include xy-cell-size(shrink, $gutter-type: none, $vertical: true);
+ }
+
+
+ @include -zf-each-breakpoint() {
+ // This is a bit of a hack/workaround, see these issues and PRs for the backstory:
+ // https://github.com/zurb/foundation-sites/issues/10244
+ // https://github.com/zurb/foundation-sites/pull/10222 and
+ // https://github.com/zurb/foundation-sites/pull/10164
+ $str: "> .#{$-zf-size}-shrink, > .#{$-zf-size}-full";
+ @for $i from 1 through $grid-columns {
+ $str: $str + ", > .#{$-zf-size}-#{$i}"
+ }
+ #{$str} {
+ flex-basis: auto;
+ }
+ }
+
+ @include -zf-each-breakpoint() {
+ // Responsive "auto" modifier
+ @if not($-zf-size == $-zf-zero-breakpoint) {
+ > .#{$-zf-size}-auto {
+ @include xy-cell(auto, $gutter-type: none, $vertical: true);
+ }
+ }
+
+ %-xy-cell-base-shrink-vertical-#{$-zf-size} {
+ @include xy-cell-base(shrink);
+ }
+
+ // Responsive "shrink" modifier
+ @if not($-zf-size == $-zf-zero-breakpoint) {
+ > .#{$-zf-size}-shrink {
+ @extend %-xy-cell-base-shrink-vertical-#{$-zf-size};
+ @include xy-cell-size(shrink, $gutter-type: none, $vertical: true);
+ }
+ }
+
+ // Responsive width modifiers
+ @for $i from 1 through $grid-columns {
+ // Sizing (percentage)
+ > .#{$-zf-size}-#{$i} {
+ @extend %-xy-cell-base-shrink-vertical-#{$-zf-size};
+ @include xy-cell-size($i, $gutter-type: none, $vertical: true);
+ }
+ }
+
+ }
+ }
+
+ @if $padding-grid {
+ .grid-padding-y {
+ // Negative margin for nested grids
+ .grid-padding-y {
+ @include xy-gutters($negative: true, $gutter-position: top bottom);
+ }
+
+ // Base cell styles
+ > .cell {
+ @include xy-gutters($gutters: $grid-padding-gutters, $gutter-type: padding, $gutter-position: top bottom);
+ }
+ }
+ }
+
+ @if $margin-grid {
+ @include xy-margin-grid-classes(top bottom, true, '.grid-margin-y');
+ }
+
+}
+
+@mixin xy-frame-grid-classes($vertical-grid: true, $margin-grid: true) {
+ // Framed grid styles
+ .grid-frame {
+ @include xy-grid-frame;
+ }
+
+ .cell .grid-frame {
+ width: 100%; // Same as include with $nested, but with less css
+ }
+
+ .cell-block {
+ @include xy-cell-block();
+ }
+
+ .cell-block-y {
+ @include xy-cell-block(true);
+ }
+
+
+ .cell-block-container {
+ @include xy-cell-block-container();
+ }
+
+
+ @include -zf-each-breakpoint(false) {
+
+ .#{$-zf-size}-grid-frame {
+ @include xy-grid-frame;
+ }
+
+ .cell .#{$-zf-size}-grid-frame {
+ width: 100%; // Same as include with $nested, but with less css
+ }
+
+ .#{$-zf-size}-cell-block {
+ @include xy-cell-block();
+ }
+
+ .#{$-zf-size}-cell-block-container {
+ @include xy-cell-block-container();
+ }
+
+ .#{$-zf-size}-cell-block-y {
+ @include xy-cell-block(true);
+ }
+ }
+
+ @if $vertical-grid {
+ .grid-y {
+ &.grid-frame {
+ width: auto;
+ @include xy-grid-frame(true);
+ }
+
+ @include -zf-each-breakpoint(false) {
+ &.#{$-zf-size}-grid-frame {
+ width: auto;
+ @include xy-grid-frame(true);
+ }
+
+ }
+ }
+ .cell {
+ .grid-y.grid-frame {
+ height: 100%; // Same as include with $nested, but with less css
+ }
+ @include -zf-each-breakpoint(false) {
+ .grid-y.#{$-zf-size}-grid-frame {
+ height: 100%; // Same as include with $nested, but with less css
+ }
+ }
+ }
+ }
+ @if $margin-grid {
+ @include xy-margin-grid-classes(top bottom, true, '.grid-margin-y');
+ .grid-frame.grid-margin-y {
+ @include xy-grid-frame(true, false, $grid-margin-gutters, $include-base: false);
+ }
+ @include -zf-each-breakpoint(false) {
+ .grid-margin-y.#{$-zf-size}-grid-frame {
+ @include xy-grid-frame(true, false, $grid-margin-gutters, $-zf-size, false);
+ }
+ }
+ }
+}
+
+// Final classes
+@mixin foundation-xy-grid-classes(
+ $base-grid: true,
+ $margin-grid: true,
+ $padding-grid: true,
+ $block-grid: true,
+ $collapse: true,
+ $offset: true,
+ $vertical-grid: true,
+ $frame-grid: true
+) {
+
+ // Base grid styles
+ @if($base-grid) {
+ @include xy-base-grid-classes();
+ }
+
+ // Margin grid
+ @if($margin-grid) {
+ @include xy-margin-grid-classes();
+ }
+
+ // Padding grid
+ @if($padding-grid) {
+ @include xy-padding-grid-classes();
+ }
+
+ // Block grid
+ @if($block-grid) {
+ @include xy-block-grid-classes($margin-grid, $padding-grid);
+ }
+
+ // Collapse gutters
+ @if($collapse) {
+ @include xy-collapse-grid-classes($margin-grid, $padding-grid);
+ }
+
+ // Offset gutters
+ @if($offset) {
+ @include xy-offset-cell-classes();
+ }
+
+ // Vertical grid
+ @if($vertical-grid) {
+ @include xy-vertical-grid-classes($margin-grid, $padding-grid);
+ }
+
+ @if ($frame-grid) {
+ @include xy-frame-grid-classes($vertical-grid, $margin-grid)
+ }
+}
diff --git a/src/foundation/xy-grid/_collapse.scss b/src/foundation/xy-grid/_collapse.scss
new file mode 100644
index 0000000..76b692f
--- /dev/null
+++ b/src/foundation/xy-grid/_collapse.scss
@@ -0,0 +1,75 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group xy-grid
+////
+
+/// Collapses the grid a cells within it.
+///
+/// @param {String} $selector [.cell] - The child element to remove the gutter from.
+/// @param {Keyword} $gutter-type [margin] - The type of gutter to remove.
+/// @param {List} $gutter-position [right left] - The positions to remove gutters from. Accepts `top`, `bottom`, `left`, `right` in any combination.
+/// @param {Keyword} $min-breakpoint [$-zf-zero-breakpoint] - Minimum breakpoint in `$breakpoint-classes` for which to collapse the gutter.
+@mixin xy-grid-collapse(
+ $selector: '.cell',
+ $gutter-type: margin,
+ $gutter-position: right left,
+ $min-breakpoint: $-zf-zero-breakpoint
+) {
+ // First, lets negate any margins on the top level
+ @if ($gutter-type == 'margin') {
+
+ @include breakpoint($min-breakpoint) {
+ @each $value in $gutter-position {
+ margin-#{$value}: 0;
+ }
+
+ > #{$selector} {
+ @each $value in $gutter-position {
+ margin-#{$value}: 0;
+ }
+ }
+ }
+
+ $excluded-bps: -zf-breakpoints-less-than($min-breakpoint);
+
+ // Output new widths to not include gutters
+ @each $bp in $breakpoint-classes {
+ @if(sl-contain($excluded-bps, $bp)) {
+ @include breakpoint($min-breakpoint) {
+ @for $i from 1 through $grid-columns {
+ // Sizing (percentage)
+ > .#{$bp}-#{$i} {
+ @include xy-cell-size($i, $gutter-type: none);
+ }
+ }
+ }
+ } @else {
+ @include breakpoint($bp) {
+ @for $i from 1 through $grid-columns {
+ // Sizing (percentage)
+ > .#{$bp}-#{$i} {
+ @include xy-cell-size($i, $gutter-type: none);
+ }
+ }
+ }
+ }
+ }
+ }
+ @else {
+
+ @include breakpoint($min-breakpoint) {
+ @each $value in $gutter-position {
+ margin-#{$value}: 0;
+ }
+
+ > #{$selector} {
+ @each $value in $gutter-position {
+ padding-#{$value}: 0;
+ }
+ }
+ }
+ }
+}
diff --git a/src/foundation/xy-grid/_frame.scss b/src/foundation/xy-grid/_frame.scss
new file mode 100644
index 0000000..9bd1d11
--- /dev/null
+++ b/src/foundation/xy-grid/_frame.scss
@@ -0,0 +1,86 @@
+/// Modifies a grid to give it "frame" behavior (no overflow, no wrap, stretch behavior)
+///
+/// @param {Boolean} $vertical [false] - Is grid vertical or horizontal. Should match grid.
+/// @param {Boolean} $nested [false] - Is grid nested or not. If nested is true this sets the frame to 100% height, otherwise will be 100vh.
+/// @param {Number|Map} $gutters [null] - Map or single value for gutters.
+/// @param {String} $breakpoint [null] - The name of the breakpoint size in your gutters map to get the size from.
+/// @param {Boolean} $include-base [true] - Include the base styles that don't vary per breakpoint.
+@mixin xy-grid-frame(
+ $vertical: false,
+ $nested: false,
+ $gutters: null,
+ $breakpoint: null,
+ $include-base: true
+) {
+
+ @if $include-base {
+ overflow: hidden;
+ position: relative;
+ flex-wrap: nowrap;
+ align-items: stretch;
+ }
+
+ @if $breakpoint == null and type-of($gutters) == 'map' {
+ @include -zf-each-breakpoint() {
+ @include xy-grid-frame($vertical, $nested, $gutters, $-zf-size, false);
+ }
+ } @else {
+ // Get our gutters if applicable
+ $gutter: -zf-get-bp-val($gutters, $breakpoint);
+
+ // If we have a gutter, add it to the width/height
+ @if $gutter {
+ @if $vertical == true {
+ $unit: if($nested == true, 100%, 100vh);
+ $gutter: rem-calc($gutter);
+ height: calc(#{$unit} + #{$gutter});
+ } @else {
+ $unit: if($nested == true, 100%, 100vw);
+ $gutter: rem-calc($gutter);
+ width: calc(#{$unit} + #{$gutter});
+ }
+ }
+ @else {
+ @if $vertical == true {
+ height: if($nested == true, 100%, 100vh);
+ } @else {
+ width: if($nested == true, 100%, 100vw);
+ }
+ }
+ }
+}
+
+/// Modifies a cell to give it "block" behavior (overflow auto, inertial scrolling)
+///
+/// @param {Boolean} $vertical [false] - Is grid vertical or horizontal. Should match grid.
+@mixin xy-cell-block(
+ $vertical: false
+) {
+ $property: if($vertical == true, 'overflow-y', 'overflow-x');
+
+ @if $vertical == true {
+ overflow-y: auto;
+ max-height: 100%;
+ min-height: 100%;
+ } @else {
+ overflow-x: auto;
+ max-width: 100%;
+ }
+
+ -webkit-overflow-scrolling: touch;
+ -ms-overflow-style: -ms-autohiding-scrollbar;
+}
+
+/// Container for inside a grid frame containing multiple blocks. Typically used
+/// as a modifier for a `.cell` to allow the cell to pass along flex sizing
+/// constraints / from parents to children.
+@mixin xy-cell-block-container() {
+ display: flex;
+ flex-direction: column;
+ max-height: 100%;
+
+ > .grid-x {
+ max-height: 100%;
+ flex-wrap: nowrap;
+ }
+}
diff --git a/src/foundation/xy-grid/_grid.scss b/src/foundation/xy-grid/_grid.scss
new file mode 100644
index 0000000..722d0b2
--- /dev/null
+++ b/src/foundation/xy-grid/_grid.scss
@@ -0,0 +1,37 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group xy-grid
+////
+
+/// Creates a max width container, designed to house your grid content.
+///
+/// @param {Number} $width [$grid-container] - a width to limit the container to.
+/// @param {Number} $padding [$grid-container-padding] - paddings of the container.
+@mixin xy-grid-container(
+ $width: $grid-container,
+ $padding: $grid-container-padding
+) {
+ @include xy-gutters($gutters: $padding, $gutter-type: padding);
+
+ max-width: $width;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/// Creates a container for your flex cells.
+///
+/// @param {Keyword} $direction [horizontal] - Either horizontal or vertical direction of cells within.
+/// @param {Boolean} $wrap [true] - If the cells within should wrap or not.
+@mixin xy-grid(
+ $direction: horizontal,
+ $wrap: true
+) {
+ $direction: if($direction == 'horizontal', row, column);
+ $wrap: if($wrap, wrap, nowrap);
+
+ display: flex;
+ flex-flow: $direction $wrap;
+}
diff --git a/src/foundation/xy-grid/_gutters.scss b/src/foundation/xy-grid/_gutters.scss
new file mode 100644
index 0000000..19e1db8
--- /dev/null
+++ b/src/foundation/xy-grid/_gutters.scss
@@ -0,0 +1,45 @@
+// Foundation for Sites by ZURB
+// foundation.zurb.com
+// Licensed under MIT Open Source
+
+////
+/// @group xy-grid
+////
+
+/// Create gutters for a cell/container.
+///
+/// @param {Number|Map} $gutters [$grid-margin-gutters] - Map or single value for gutters.
+/// @param {Keyword} $gutter-type [margin] - Type of gutter to output. Accepts either margin or padding.
+/// @param {List} $gutter-position [right left] - The position to apply gutters to. Accepts `top`, `bottom`, `left`, `right` in any combination.
+/// @param {Boolean} $negative [false] - Whether to apply the gutter as a negative value. Commonly used for nested grids.
+@mixin xy-gutters(
+ $gutters: $grid-margin-gutters,
+ $gutter-type: margin,
+ $gutter-position: right left,
+ $negative: false
+) {
+ $operator: if($negative, '-', '');
+
+ // If we have declared negative gutters, force type to `margin.
+ $gutter-type: if($negative, 'margin', $gutter-type);