├── .eslintignore ├── .eslintrc ├── .gitignore ├── README.md ├── footer.php ├── functions.php ├── gulpfile.js ├── header.php ├── index.php ├── js ├── actions │ ├── nav-actions.js │ ├── post-actions.js │ └── term-actions.js ├── components │ ├── controller.jsx │ ├── navigation │ │ ├── index.jsx │ │ └── style.scss │ ├── pagination │ │ ├── index.jsx │ │ └── style.scss │ ├── post │ │ ├── index.jsx │ │ └── style.scss │ ├── posts │ │ ├── index.jsx │ │ ├── list.jsx │ │ ├── single.jsx │ │ └── style.scss │ ├── search │ │ └── index.jsx │ └── term │ │ ├── index.jsx │ │ └── style.scss ├── constants │ └── constants.js ├── customize-preview.js ├── dispatcher │ └── dispatcher.js ├── index.jsx ├── stores │ ├── navigation-store.js │ ├── posts-store.js │ └── term-store.js └── utils │ ├── api.js │ └── content-mixin.js ├── package.json ├── sass ├── _normalize.scss ├── elements │ ├── _elements.scss │ ├── _lists.scss │ └── _tables.scss ├── forms │ ├── _buttons.scss │ ├── _fields.scss │ └── _forms.scss ├── media │ ├── _captions.scss │ ├── _galleries.scss │ └── _media.scss ├── mixins │ └── _mixins-master.scss ├── modules │ ├── _accessibility.scss │ ├── _alignments.scss │ ├── _clearings.scss │ └── _infinite-scroll.scss ├── navigation │ ├── _links.scss │ └── _navigation.scss ├── site │ ├── _site.scss │ ├── _structure.scss │ └── primary │ │ ├── _footer.scss │ │ └── _header.scss ├── style.scss ├── typography │ ├── _copy.scss │ ├── _headings.scss │ └── _typography.scss └── variables-site │ ├── _colors.scss │ ├── _data-uris.scss │ ├── _structure.scss │ ├── _typography.scss │ └── _variables-site.scss ├── screenshot.png ├── sidebar.php └── webpack.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | js/app.js 2 | gulpfile.js -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "es6": true, 6 | "mocha": true, 7 | "node": true 8 | }, 9 | "plugins": [ 10 | "eslint-plugin-react" 11 | ], 12 | "rules": { 13 | "array-bracket-spacing": [ 1, "always" ], 14 | "brace-style": [ 1, "1tbs" ], 15 | // REST API objects include underscores 16 | "camelcase": 0, 17 | "comma-spacing": 1, 18 | "curly": 1, 19 | "dot-notation": 1, 20 | "computed-property-spacing": [ 1, "always" ], 21 | "eol-last": 1, 22 | "indent": [ 1, "tab", { "SwitchCase": 1 } ], 23 | "jsx-quotes": [ 1, "prefer-double" ], 24 | "key-spacing": 1, 25 | "keyword-spacing": 1, 26 | "max-len": [ 1, { "code": 140 } ], 27 | "new-cap": [ 1, { "capIsNew": false, "newIsCap": true } ], 28 | "no-console": 1, 29 | "no-else-return": 1, 30 | "no-extra-semi": 1, 31 | "no-lonely-if": 1, 32 | "no-mixed-spaces-and-tabs": 1, 33 | "no-multiple-empty-lines": [ 1, { "max": 1 } ], 34 | "no-multi-spaces": 1, 35 | "no-nested-ternary": 1, 36 | "no-new": 1, 37 | "no-process-exit": 1, 38 | "no-redeclare": 1, 39 | "no-shadow": 1, 40 | "no-spaced-func": 1, 41 | "no-trailing-spaces": 1, 42 | "no-unreachable": 1, 43 | // Allows Chai `expect` expressions 44 | "no-unused-expressions": 0, 45 | "no-unused-vars": 1, 46 | "no-var": 1, 47 | "object-curly-spacing": [ 1, "always" ], 48 | "operator-linebreak": [ 1, "after", { "overrides": { 49 | "?": "after", 50 | ":": "after" 51 | } } ], 52 | "padded-blocks": [ 1, "never" ], 53 | "prefer-const": 1, 54 | "quote-props": [ 1, "as-needed", { "keywords": true } ], 55 | "quotes": [ 1, "single", "avoid-escape" ], 56 | "react/jsx-uses-react": 1, 57 | "react/jsx-uses-vars": 1, 58 | "react/jsx-no-duplicate-props": 1, 59 | "react/no-did-mount-set-state": 1, 60 | "react/no-did-update-set-state": 1, 61 | "react/no-is-mounted": 1, 62 | "react/jsx-no-bind": 1, 63 | "react/jsx-curly-spacing": [ 1, "always" ], 64 | "semi": 1, 65 | "semi-spacing": 1, 66 | "space-before-blocks": [ 1, "always" ], 67 | "space-before-function-paren": [ 1, "never" ], 68 | "space-in-parens": [ 1, "always" ], 69 | "space-infix-ops": [ 1, { "int32Hint": false } ], 70 | "space-unary-ops": [ 1, { 71 | "overrides": { 72 | "!": true 73 | } 74 | } ], 75 | "valid-jsdoc": [ 1, { "requireReturn": false } ], 76 | // Common top-of-file requires, expressions between external, interal 77 | "vars-on-top": 1, 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | js/app.js 3 | js/app.js.map 4 | style.css 5 | style.css.map 6 | sass-cache 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Anadama 2 | ======= 3 | 4 | A react-based recipe theme for WordPress. 5 | 6 | Want to try out the theme? Download or clone this repo into your `/themes` folder, then run a few npm scripts to install and build the javascript & CSS files. The process will look like this 7 | 8 | git clone https://github.com/ryelle/Anadama-React anadama 9 | cd anadama 10 | npm install 11 | npm run dev 12 | 13 | Now you'll see a `js/app.js` file in the theme, and it will be available for you to switch to in wp-admin. If you're having trouble getting the theme active, please [file an issue](https://github.com/ryelle/Anadama-React/issues) & I'll help you out. 14 | 15 | _If you don't have npm or gulp installed, you can find instructions on their websites: [gulp](http://gulpjs.com/), [npm](http://npmjs.com)._ 16 | 17 | Setup 18 | ----- 19 | 20 | Since this is a more "experimental" theme, you'll need to have a few things set up before it'll work. 21 | 22 | 1. You'll need the [WP REST API plugin version 2.0-beta13](https://wordpress.org/plugins/rest-api/). WP 4.4+ has the framework for the REST API, but the actual content of it still requires the plugin. 23 | 2. You'll also need this [WP-API Menus plugin](https://wordpress.org/plugins/wp-api-menus/). The REST API doesn't provide an endpoint for menus, so another plugin is necessary. 24 | 3. Your permalinks will need to be set to `/%year%/%monthnum%/%postname%/`. Single-post/page views will not work without permalinks set. 25 | 26 | Display & Features 27 | ------------------ 28 | 29 | The front page of this theme displays 10 categories (ordered by term ID, which corresponds to date created) and the 20 most recent posts in each category. It uses localStorage to hold posts for offline/faster response time. 30 | 31 | This theme has **no** sidebars, widgets, or comments. 32 | 33 | Known Issues/To Do 34 | ------------------ 35 | 36 | - Sites that have no content won't appear to load, the loading message stays on-screen even after an empty server response. 37 | - Menu items have conditional classes in place, but they're not automatically applied (current page, parent, etc) 38 | - Work with Jetpack scripts to load (or not) when needed. 39 | - Make the recipe's "Print" button work (related to Jetpack scripts) 40 | - Responsive styles + a good print stylesheet for recipe pages 41 | - Cached data will stick around forever, maybe not the best idea for post list pages. 42 | - Category/Tag archives don't exist yet 43 | 44 | Building the Javascript & CSS 45 | ----------------------------- 46 | 47 | Currently this project uses gulp to build a CSS file from `sass/style.scss`, and webpack by way of gulp to build the javascript file from `js/index.js`. There are 3 ways to build (the CSS is done the same in all 3, no minification there currently): 48 | 49 | ### `npm run dev` 50 | 51 | This builds a development version of app.js, along with a sourcemap. 52 | 53 | ### `npm run watch` 54 | 55 | This builds the development version, then watches the Sass and JS files for any changes to automatically re-build. 56 | 57 | ### `npm run build` 58 | 59 | This builds the "production" version of the code, and is only here as an example of how to set up a production build. I don't recommend running Anadama as it is in production without your own testing (browser, device, etc). 60 | -------------------------------------------------------------------------------- /footer.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | | 20 | Kelly Dwan & Mel Choyce' ); ?> 21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | tag in the document head, and expect WordPress to 38 | * provide it for us. 39 | */ 40 | add_theme_support( 'title-tag' ); 41 | 42 | /* 43 | * Enable support for Post Thumbnails on posts and pages. 44 | * 45 | * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/ 46 | */ 47 | add_theme_support( 'post-thumbnails' ); 48 | 49 | // This theme uses wp_nav_menu() in one location. 50 | register_nav_menus( array( 51 | 'primary' => esc_html__( 'Primary Menu', 'anadama' ), 52 | ) ); 53 | 54 | /* 55 | * Switch default core markup for search form, comment form, and comments 56 | * to output valid HTML5. 57 | */ 58 | add_theme_support( 'html5', array( 59 | 'search-form', 60 | 'comment-form', 61 | 'comment-list', 62 | 'gallery', 63 | 'caption', 64 | ) ); 65 | 66 | /* 67 | * Enable support for Post Formats. 68 | * See https://developer.wordpress.org/themes/functionality/post-formats/ 69 | */ 70 | add_theme_support( 'post-formats', array( 71 | 'aside', 72 | 'image', 73 | 'video', 74 | 'quote', 75 | 'link', 76 | ) ); 77 | 78 | add_post_type_support( 'post', 'comments' ); 79 | add_post_type_support( 'page', 'comments' ); 80 | } 81 | endif; // anadama_setup 82 | add_action( 'after_setup_theme', 'anadama_setup' ); 83 | 84 | /** 85 | * Set the content width in pixels, based on the theme's design and stylesheet. 86 | * 87 | * Priority 0 to make it available to lower priority callbacks. 88 | * 89 | * @global int $content_width 90 | */ 91 | function anadama_content_width() { 92 | $GLOBALS['content_width'] = apply_filters( 'anadama_content_width', 730 ); 93 | } 94 | add_action( 'after_setup_theme', 'anadama_content_width', 0 ); 95 | 96 | /** 97 | * Enqueue scripts and styles. 98 | */ 99 | function anadama_scripts() { 100 | if ( is_customize_preview() ) { 101 | wp_enqueue_script( 'anadama-customize-preview', get_template_directory_uri() . '/js/customize-preview.js', array( 'jquery', 'customize-preview', 'customize-preview-nav-menus' ), ANADAMA_VERSION, true ); 102 | } 103 | 104 | wp_enqueue_style( 'anadama-style', get_stylesheet_uri() ); 105 | wp_enqueue_script( 'anadama-react', get_template_directory_uri() . '/js/app.js', array( 'jquery' ), ANADAMA_VERSION, true ); 106 | 107 | $url = trailingslashit( home_url() ); 108 | $path = trailingslashit( parse_url( $url, PHP_URL_PATH ) ); 109 | 110 | wp_scripts()->add_data( 'anadama-react', 'data', sprintf( 'var AnadamaSettings = %s;', wp_json_encode( array( 111 | 'nonce' => wp_create_nonce( 'wp_rest' ), 112 | 'localStorageCache' => ! is_customize_preview(), 113 | 'user' => get_current_user_id(), 114 | 'title' => get_bloginfo( 'name', 'display' ), 115 | 'path' => $path, 116 | 'URL' => array( 117 | 'api' => esc_url_raw( get_rest_url( null, '/wp/v2' ) ), 118 | 'menuApi' => esc_url_raw( get_rest_url( null, '/wp-api-menus/v2' ) ), 119 | 'root' => esc_url_raw( $url ), 120 | ), 121 | ) ) ) ); 122 | } 123 | add_action( 'wp_enqueue_scripts', 'anadama_scripts' ); 124 | 125 | /** 126 | * Returns the Google font stylesheet URL, if available. 127 | * 128 | * The use of Source Serif Pro and Source Code Pro by default is 129 | * localized. For languages that use characters not supported by 130 | * either font, the font can be disabled. 131 | * 132 | * @return string Font stylesheet or empty string if disabled. 133 | */ 134 | function anadama_fonts_url() { 135 | $fonts_url = ''; 136 | 137 | /* Translators: If there are characters in your language that are not 138 | * supported by Source Serif Pro, translate this to 'off'. Do not translate 139 | * into your own language. 140 | */ 141 | $serifpro = _x( 'on', 'Source Serif Pro font: on or off', 'anadama' ); 142 | 143 | /* Translators: If there are characters in your language that are not 144 | * supported by Source Code Pro, translate this to 'off'. Do not translate into 145 | * your own language. 146 | */ 147 | $codepro = _x( 'on', 'Source Code Pro font: on or off', 'anadama' ); 148 | 149 | if ( 'off' !== $serifpro || 'off' !== $codepro ) { 150 | $font_families = array(); 151 | 152 | if ( 'off' !== $serifpro ) 153 | $font_families[] = urlencode( 'Source Serif Pro:400,700' ); 154 | 155 | if ( 'off' !== $codepro ) 156 | $font_families[] = urlencode( 'Source Code Pro:400,600' ); 157 | 158 | $protocol = is_ssl() ? 'https' : 'http'; 159 | $query_args = array( 160 | 'family' => implode( '|', $font_families ), 161 | 'subset' => urlencode( 'latin,latin-ext' ), 162 | ); 163 | $fonts_url = add_query_arg( $query_args, "$protocol://fonts.googleapis.com/css" ); 164 | } 165 | 166 | return $fonts_url; 167 | } 168 | 169 | /** 170 | * Loads our special font CSS file. 171 | * 172 | * To disable in a child theme, use wp_dequeue_style() 173 | * function mytheme_dequeue_fonts() { 174 | * wp_dequeue_style( 'anadama-fonts' ); 175 | * } 176 | * add_action( 'wp_enqueue_scripts', 'mytheme_dequeue_fonts', 11 ); 177 | * 178 | * @return void 179 | */ 180 | function anadama_fonts() { 181 | $fonts_url = anadama_fonts_url(); 182 | if ( ! empty( $fonts_url ) ) 183 | wp_enqueue_style( 'anadama-fonts', esc_url_raw( $fonts_url ), array(), null ); 184 | } 185 | add_action( 'wp_enqueue_scripts', 'anadama_fonts' ); 186 | 187 | /** 188 | * Add theme support for Jetpack Features 189 | */ 190 | function anadama_jetpack_setup() { 191 | add_theme_support( 'site-logo' ); 192 | } 193 | add_action( 'after_setup_theme', 'anadama_jetpack_setup' ); 194 | 195 | /** 196 | * Register customizer settings. 197 | * 198 | * @param WP_Customize_Manager $wp_customize Customize manager. 199 | */ 200 | function anadama_customize_register( WP_Customize_Manager $wp_customize ) { 201 | $wp_customize->get_setting( 'blogname' )->transport = 'postMessage'; 202 | $wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage'; 203 | 204 | add_filter( 'wp_get_nav_menu_items', '_anadama_filter_wp_api_nav_menu_items_workaround', 20 ); 205 | } 206 | add_action( 'customize_register', 'anadama_customize_register' ); 207 | 208 | /** 209 | * Workaround issue in WP API Menus plugin to force nav menu item classes to be arrays instead of strings. 210 | * 211 | * @see \WP_REST_Menus::get_menu_location() 212 | * 213 | * @param array $items Nav menu items. 214 | */ 215 | function _anadama_filter_wp_api_nav_menu_items_workaround( $items ) { 216 | foreach ( $items as &$item ) { 217 | if ( is_string( $item->classes ) ) { 218 | $item->classes = explode( ' ', $item->classes ); 219 | } 220 | } 221 | return $items; 222 | } 223 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var path = require('path'); 5 | var gutil = require('gulp-util'); 6 | var webpack = require('webpack'); 7 | var sass = require('gulp-sass'); 8 | var autoprefixer = require('gulp-autoprefixer'); 9 | 10 | function onBuild( done ) { 11 | return function( err, stats ) { 12 | if ( err ) { 13 | throw new gutil.PluginError( 'webpack', err ); 14 | } 15 | 16 | gutil.log( stats.toString( { 17 | colors: true, 18 | chunks: false, 19 | } ) ); 20 | 21 | if ( done ) { 22 | done(); 23 | } 24 | }; 25 | } 26 | 27 | function getWebpackConfig() { 28 | var config = Object.create( require( './webpack.config.js' ) ); 29 | 30 | config.entry = { 31 | app: './js/index.jsx', 32 | // vendor: [ 'babel-core/polyfill', 'classnames', 'moment', 'page' ] 33 | }; 34 | 35 | return config; 36 | } 37 | 38 | function doSass() { 39 | if ( arguments.length ) { 40 | console.log('Sass file ' + arguments[0].path + ' changed.'); 41 | } 42 | var start = new Date(); 43 | console.log('Building CSS bundle'); 44 | gulp.src( './sass/style.scss' ) 45 | .pipe( sass().on( 'error', sass.logError ) ) 46 | .pipe( autoprefixer() ) 47 | .pipe( gulp.dest( './' ) ) 48 | .on( 'end', function() { 49 | console.log( 'CSS finished.' ); 50 | } ); 51 | }; 52 | 53 | gulp.task( 'sass:build', function() { 54 | doSass(); 55 | } ); 56 | 57 | gulp.task( 'sass:watch', function() { 58 | doSass(); 59 | gulp.watch( [ './sass/**/*.scss', './js/**/*.scss' ], doSass ); 60 | } ); 61 | 62 | gulp.task( 'react:build', function( done ) { 63 | webpack( getWebpackConfig() ).run( onBuild( done ) ); 64 | } ); 65 | 66 | gulp.task( 'react:watch', function() { 67 | webpack( getWebpackConfig() ).watch( 100, onBuild() ); 68 | } ); 69 | 70 | gulp.task( 'default', ['react:build', 'sass:build'] ); 71 | gulp.task( 'watch', ['react:watch', 'sass:watch'] ); 72 | -------------------------------------------------------------------------------- /header.php: -------------------------------------------------------------------------------- 1 | section and everything up until
6 | * 7 | * @link https://developer.wordpress.org/themes/basics/template-files/#template-partials 8 | * 9 | * @package Anadama 10 | */ 11 | 12 | ?> 13 | > 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | > 24 |
25 | 26 | 27 |
28 |
29 | 30 |
31 | 32 | 33 |

34 | 35 | 37 |

38 | 39 |
40 |
41 | 42 |
43 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /js/actions/nav-actions.js: -------------------------------------------------------------------------------- 1 | import AppDispatcher from '../dispatcher/dispatcher'; 2 | import AppConstants from '../constants/constants'; 3 | 4 | export default { 5 | /** 6 | * @param {array} menu 7 | */ 8 | fetch: function( menu ) { 9 | AppDispatcher.handleViewAction( { 10 | actionType: AppConstants.REQUEST_NAV_SUCCESS, 11 | data: menu 12 | } ); 13 | }, 14 | 15 | /** 16 | * @param {array} menu 17 | */ 18 | fetchFailed: function( message, request ) { 19 | AppDispatcher.handleViewAction( { 20 | actionType: AppConstants.REQUEST_NAV_ERROR, 21 | message: message, 22 | data: request 23 | } ); 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /js/actions/post-actions.js: -------------------------------------------------------------------------------- 1 | import AppDispatcher from '../dispatcher/dispatcher'; 2 | import AppConstants from '../constants/constants'; 3 | 4 | export default { 5 | /** 6 | * @param {array} posts 7 | */ 8 | fetch: function( posts ) { 9 | AppDispatcher.handleViewAction( { 10 | actionType: AppConstants.REQUEST_POSTS_SUCCESS, 11 | data: posts 12 | } ); 13 | }, 14 | 15 | fetchPaginationLimit: function( total ) { 16 | AppDispatcher.handleViewAction( { 17 | actionType: AppConstants.REQUEST_PAGINATION_LIMIT, 18 | data: total 19 | } ); 20 | }, 21 | 22 | /** 23 | * @param {array} posts 24 | */ 25 | fetchSingle: function( post ) { 26 | AppDispatcher.handleViewAction( { 27 | actionType: AppConstants.REQUEST_POST_SUCCESS, 28 | id: post.id, 29 | data: post 30 | } ); 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /js/actions/term-actions.js: -------------------------------------------------------------------------------- 1 | import AppDispatcher from '../dispatcher/dispatcher'; 2 | import AppConstants from '../constants/constants'; 3 | 4 | export default { 5 | /** 6 | * @param {object} term 7 | */ 8 | fetch: function( term ) { 9 | AppDispatcher.handleViewAction( { 10 | actionType: AppConstants.REQUEST_TERM_SUCCESS, 11 | id: term.id, 12 | data: term 13 | } ); 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /js/components/controller.jsx: -------------------------------------------------------------------------------- 1 | // React 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import classNames from 'classnames'; 5 | 6 | // Components 7 | import Navigation from './navigation'; 8 | import CategoryList from './posts'; 9 | import SinglePost from './post'; 10 | import Term from './term'; 11 | 12 | // Private vars 13 | var _currentSlug, _currentType, _currentPage; 14 | 15 | let Controller = { 16 | setup: function( context, next ) { 17 | var path = context.pathname; 18 | 19 | if ( path.match( /\/wp-admin/i ) ) { 20 | next(); 21 | } 22 | 23 | if ( path.substr( -1 ) === '/' ) { 24 | path = path.substr( 0, path.length - 1 ); 25 | } 26 | if ( path.length ) { 27 | _currentSlug = path.substring( path.lastIndexOf( '/' ) + 1 ); 28 | } 29 | 30 | _currentType = 'post'; 31 | if ( ! path.match( /\d{4}\/\d{2}/ ) ) { 32 | _currentType = 'page'; 33 | } 34 | 35 | if ( 'undefined' !== typeof context.params.term ) { 36 | _currentSlug = context.params.term; 37 | _currentType = 'categories'; 38 | } 39 | 40 | let bodyClass = { 41 | 'logged-in': ( parseInt( AnadamaSettings.user ) !== 0 ), 42 | 'home': ( path.length === 0 ), 43 | 'single': ( path.length > 0 ), 44 | 'single-page': ( path.length > 0 ) && ( 'page' === _currentType ), 45 | 'single-post': ( path.length > 0 ) && ( 'post' === _currentType ), 46 | }; 47 | document.body.className = classNames( bodyClass ); 48 | 49 | _currentPage = parseInt( context.params.page ) || 1; 50 | 51 | next(); 52 | }, 53 | 54 | navigation: function( context, next ) { 55 | ReactDOM.render( 56 | , 57 | document.getElementById( 'site-navigation' ) 58 | ); 59 | 60 | next(); 61 | }, 62 | 63 | posts: function() { 64 | ReactDOM.render( 65 | , 66 | document.getElementById( 'main' ) 67 | ); 68 | }, 69 | 70 | term: function() { 71 | ReactDOM.render( 72 | , 73 | document.getElementById( 'main' ) 74 | ); 75 | }, 76 | 77 | post: function() { 78 | ReactDOM.render( 79 | , 80 | document.getElementById( 'main' ) 81 | ); 82 | }, 83 | }; 84 | 85 | export default Controller; 86 | -------------------------------------------------------------------------------- /js/components/navigation/index.jsx: -------------------------------------------------------------------------------- 1 | // External dependencies 2 | import React from 'react'; 3 | import classNames from 'classnames'; 4 | 5 | // Internal dependencies 6 | import API from 'utils/api'; 7 | import NavigationStore from '../../stores/navigation-store'; 8 | 9 | let SubMenu = React.createClass( { 10 | render: function() { 11 | let menu = this.props.items.map( function( item, i ) { 12 | return 13 | } ); 14 | 15 | return ( 16 |
    17 | { menu } 18 |
19 | ); 20 | } 21 | } ); 22 | 23 | let MenuItem = React.createClass( { 24 | blur: function( event ) { 25 | event.target.blur(); 26 | }, 27 | 28 | toggleFocus: function( event ) { 29 | var self = event.target; 30 | 31 | // Move up through the ancestors of the current link until we hit .nav-menu. 32 | while ( -1 === self.className.indexOf( 'nav-menu' ) ) { 33 | // On li elements toggle the class .focus. 34 | if ( 'li' === self.tagName.toLowerCase() ) { 35 | if ( -1 !== self.className.indexOf( 'focus' ) ) { 36 | self.className = self.className.replace( ' focus', '' ); 37 | } else { 38 | self.className += ' focus'; 39 | } 40 | } 41 | 42 | self = self.parentElement; 43 | } 44 | }, 45 | 46 | render: function() { 47 | let re; 48 | if ( location.pathname !== '/' ) { 49 | re = new RegExp( location.pathname + '$' ); 50 | } else { 51 | re = new RegExp( location.hostname + '/$' ); 52 | } 53 | let classes = classNames( { 54 | 'menu-item': true, 55 | 'menu-item-has-children': this.props.item.children.length, 56 | 'current-menu-item': ( location.pathname === this.props.item.url ) || re.test( this.props.item.url ), 57 | 'current-menu-ancestor': false, 58 | 'current-menu-parent': false, 59 | }, this.props.item.classes ); 60 | 61 | return ( 62 |
  • 0 }> 63 | { this.props.item.title } 64 | { this.props.item.children.length ? 65 | : 66 | null 67 | } 68 |
  • 69 | ); 70 | } 71 | } ); 72 | 73 | /* 74 | * Method to retrieve state from Stores 75 | */ 76 | function getState() { 77 | return { 78 | data: NavigationStore.getMenu(), 79 | isMenuOpen: false, 80 | }; 81 | } 82 | 83 | let Navigation = React.createClass( { 84 | getInitialState: function() { 85 | return getState(); 86 | }, 87 | 88 | componentDidMount: function() { 89 | API.getMenu( '/menu-locations/primary/' ); 90 | NavigationStore.addChangeListener( this._onChange ); 91 | }, 92 | 93 | componentWillUnmount: function() { 94 | NavigationStore.removeChangeListener( this._onChange ); 95 | }, 96 | 97 | _onChange: function() { 98 | this.setState( getState() ); 99 | }, 100 | 101 | toggleMenu: function( event ) { 102 | event.preventDefault(); 103 | this.setState( { isMenuOpen: ! this.state.isMenuOpen } ); 104 | }, 105 | 106 | render: function() { 107 | let menu = this.state.data.map( function( item, i ) { 108 | return 109 | } ); 110 | 111 | let menuClasses = classNames( { 112 | 'menu-container': true, 113 | 'menu-open': this.state.isMenuOpen, 114 | } ); 115 | 116 | return ( 117 |
    118 |
    119 | 120 |
    121 |
      122 | { menu } 123 |
    124 |
    125 | ); 126 | } 127 | } ); 128 | 129 | export default Navigation; 130 | -------------------------------------------------------------------------------- /js/components/navigation/style.scss: -------------------------------------------------------------------------------- 1 | .main-navigation { 2 | clear: both; 3 | display: inline-block; 4 | margin-bottom: 75px; 5 | @include font-size( 16 ); 6 | 7 | ul { 8 | list-style: none; 9 | margin: 0; 10 | padding-left: 0; 11 | 12 | ul { 13 | float: left; 14 | position: absolute; 15 | top: 55px; 16 | left: -999em; 17 | z-index: 99999; 18 | min-width: 200px; 19 | background: $color__background-header; 20 | text-align: left; 21 | 22 | ul { 23 | position: relative; 24 | top: auto; 25 | left: auto; 26 | } 27 | 28 | a { 29 | display: inline-block; 30 | margin: 0 0 5px 15px; 31 | padding: 5px 25px; 32 | width: auto; 33 | line-height: 1.2; 34 | } 35 | } 36 | 37 | li:hover > ul, 38 | li.focus > ul { 39 | left: auto; 40 | } 41 | } 42 | 43 | li { 44 | float: left; 45 | position: relative; 46 | } 47 | 48 | .nav-menu > .menu-item-has-children > a:after { 49 | content: ' '; 50 | position: absolute; 51 | top: 28px; 52 | right: 5px; 53 | height: 10px; 54 | width: 10px; 55 | background: url($icon__arrow) no-repeat center; 56 | background-size: contain; 57 | } 58 | 59 | a { 60 | display: block; 61 | margin: 10px 0; 62 | padding: 10px 25px 0; 63 | text-decoration: none; 64 | } 65 | 66 | .nav-menu > .current_page_item > a, 67 | .nav-menu > .current-menu-item > a, 68 | .nav-menu > .current-menu-ancestor > a { 69 | border-bottom: 1px dotted white; 70 | } 71 | 72 | a:hover, 73 | a:active, 74 | a:focus, 75 | .focus > a:focus { 76 | outline: none; 77 | text-decoration: underline; 78 | } 79 | } 80 | 81 | /* Small menu. */ 82 | .menu-toggle { 83 | display: none; 84 | } 85 | 86 | @media screen and (max-width: 600px) { 87 | .menu-toggle, 88 | .main-navigation.toggled .nav-menu { 89 | display: block; 90 | } 91 | 92 | .main-navigation ul { 93 | display: none; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /js/components/pagination/index.jsx: -------------------------------------------------------------------------------- 1 | // External dependencies 2 | import React from 'react'; 3 | 4 | let Post = React.createClass( { 5 | propTypes: { 6 | start: React.PropTypes.number, 7 | current: React.PropTypes.number, 8 | end: React.PropTypes.number.isRequired, 9 | }, 10 | 11 | getDefaultProps: function(){ 12 | return { 13 | start: 1, 14 | current: 1 15 | }; 16 | }, 17 | 18 | render: function() { 19 | let next = this.props.current + 1; 20 | let prev = this.props.current - 1; 21 | 22 | return ( 23 |
    24 |
    25 | { ( prev > 0 ) ? 26 |
    27 | { ( prev === 1 ) ? 28 | Previous Page : 29 | Previous Page 30 | } 31 |
    : 32 | null 33 | } 34 | { ( next <= this.props.end ) ? 35 |
    36 | Next Page 37 |
    : 38 | null 39 | } 40 |
    41 |
    42 | ); 43 | } 44 | } ); 45 | 46 | export default Post; 47 | -------------------------------------------------------------------------------- /js/components/pagination/style.scss: -------------------------------------------------------------------------------- 1 | 2 | .posts-navigation, 3 | .post-navigation { 4 | margin: 0 auto 50px; 5 | max-width: 660px; 6 | } 7 | 8 | .posts-navigation .nav-previous, 9 | .post-navigation .nav-previous { 10 | float: left; 11 | width: 50%; 12 | } 13 | 14 | .posts-navigation .nav-next, 15 | .post-navigation .nav-next { 16 | float: right; 17 | text-align: right; 18 | width: 50%; 19 | } 20 | -------------------------------------------------------------------------------- /js/components/post/index.jsx: -------------------------------------------------------------------------------- 1 | // External dependencies 2 | import React from 'react'; 3 | import classNames from 'classnames'; 4 | import page from 'page'; 5 | 6 | // Internal dependencies 7 | import API from 'utils/api'; 8 | import ContentMixin from 'utils/content-mixin'; 9 | import PostsStore from '../../stores/posts-store'; 10 | 11 | /** 12 | * Method to retrieve state from Stores 13 | */ 14 | function getState( id ) { 15 | return { 16 | data: PostsStore.getPost( id ) 17 | }; 18 | } 19 | 20 | let SinglePost = React.createClass( { 21 | mixins: [ ContentMixin ], 22 | 23 | propTypes: { 24 | slug: React.PropTypes.string.isRequired, 25 | type: React.PropTypes.oneOf( [ 'post', 'page' ] ), 26 | }, 27 | 28 | getDefaultProps: function(){ 29 | return { 30 | type: 'post', 31 | }; 32 | }, 33 | 34 | getInitialState: function() { 35 | return getState( this.props.slug ); 36 | }, 37 | 38 | componentDidMount: function() { 39 | PostsStore.addChangeListener( this._onChange ); 40 | API.getPost( this.props.slug, this.props.type ); 41 | }, 42 | 43 | componentDidUpdate: function( prevProps, prevState ) { 44 | if ( prevProps !== this.props ) { 45 | API.getPost( this.props.slug, this.props.type ); 46 | } 47 | }, 48 | 49 | componentWillUnmount: function() { 50 | PostsStore.removeChangeListener( this._onChange ); 51 | }, 52 | 53 | _onChange: function() { 54 | this.setState( getState( this.props.slug ) ); 55 | }, 56 | 57 | setTitle: function() { 58 | let post = this.state.data; 59 | if ( 'undefined' !== typeof post.title ) { 60 | document.title = `${post.title.rendered} — ${AnadamaSettings.title}`; 61 | } 62 | }, 63 | 64 | close: function( event ) { 65 | page( '/' ); 66 | }, 67 | 68 | renderPlaceholder: function() { 69 | return null; 70 | }, 71 | 72 | render: function() { 73 | let post = this.state.data; 74 | if ( 'undefined' === typeof post.title ) { 75 | return this.renderPlaceholder(); 76 | } 77 | 78 | this.setTitle(); 79 | 80 | let classes = classNames( { 81 | 'entry': true 82 | } ); 83 | 84 | return ( 85 |
    86 |
    87 | < Back 88 |

    89 |
    90 |

    91 |
    92 | ); 93 | } 94 | } ); 95 | 96 | export default SinglePost; 97 | -------------------------------------------------------------------------------- /js/components/post/style.scss: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | Single Posts & Pages 3 | --------------------------------------------------------------*/ 4 | 5 | .card { 6 | position: relative; 7 | margin: 0 auto; 8 | padding: 0 60px 60px; 9 | width: $size__site-post; 10 | background-color: white; 11 | } 12 | 13 | .card-x { 14 | position: absolute; 15 | left: 60px; 16 | top: 10px; 17 | cursor: pointer; 18 | } 19 | 20 | .site-main .entry-content { 21 | padding-left: 25%; 22 | position: relative; 23 | 24 | .jetpack-recipe { 25 | margin: 0; 26 | padding: 0; 27 | border: none; 28 | border-radius: 0; 29 | } 30 | 31 | .jetpack-recipe-title { 32 | display: none; 33 | } 34 | 35 | .jetpack-recipe-meta { 36 | position: absolute; 37 | top: 0; 38 | left: 0; 39 | margin: 18px 0 0; 40 | padding-right: 20px; 41 | width: 25%; 42 | @include font-size( $font__size-recipe-meta ); 43 | list-style: none; 44 | 45 | li { 46 | float: none; 47 | margin: 0 0 15px; 48 | } 49 | 50 | strong { 51 | display: block; 52 | } 53 | } 54 | 55 | .jetpack-recipe-print { 56 | text-align: left; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /js/components/posts/index.jsx: -------------------------------------------------------------------------------- 1 | /* global AnadamaSettings */ 2 | // External dependencies 3 | import React from 'react'; 4 | import filter from 'lodash/filter'; 5 | import clone from 'lodash/clone'; 6 | import isEqual from 'lodash/isEqual'; 7 | 8 | // Internal dependencies 9 | import API from 'utils/api'; 10 | import PostsStore from '../../stores/posts-store'; 11 | import PostList from './list'; 12 | import SearchForm from '../search'; 13 | import Pagination from '../pagination'; 14 | 15 | /** 16 | * Method to retrieve state from Stores 17 | */ 18 | function getState() { 19 | return { 20 | data: PostsStore.getPostsByCategory(), 21 | paginationLimit: PostsStore.getPaginationLimit(), 22 | filter: '', 23 | }; 24 | } 25 | 26 | let CategoryList = React.createClass( { 27 | 28 | propTypes: { 29 | page: React.PropTypes.number.isRequired, 30 | }, 31 | 32 | getInitialState: function() { 33 | return getState(); 34 | }, 35 | 36 | componentDidMount: function() { 37 | PostsStore.addChangeListener( this._onChange ); 38 | API.getPosts( { page: this.props.page } ); 39 | }, 40 | 41 | componentDidUpdate: function( prevProps ) { 42 | if ( ! isEqual( prevProps, this.props ) ) { 43 | API.getPosts( { page: this.props.page } ); 44 | } 45 | }, 46 | 47 | componentWillUnmount: function() { 48 | PostsStore.removeChangeListener( this._onChange ); 49 | }, 50 | 51 | _onChange: function() { 52 | this.setState( getState() ); 53 | }, 54 | 55 | setTitle: function() { 56 | document.title = AnadamaSettings.title; 57 | }, 58 | 59 | search: function() { 60 | let term = this.refs.searchForm.getValue(); 61 | this.setState( { 62 | filter: term, 63 | } ); 64 | }, 65 | 66 | getPosts: function() { 67 | if ( ! this.state.filter || ( this.state.filter.length < 3 ) ) { 68 | return this.state.data; 69 | } 70 | 71 | let categories = clone( this.state.data, true ); 72 | 73 | categories = categories.map( ( category ) => { 74 | let filteredPosts = filter( category.posts, ( post ) => { 75 | if ( 'undefined' === typeof post.title ) { 76 | return false; 77 | } 78 | let title = post.title.rendered.toLowerCase(); 79 | let search = this.state.filter.toLowerCase(); 80 | return ( -1 !== title.indexOf( search ) ); 81 | } ); 82 | category.posts = filteredPosts; 83 | return category; 84 | } ); 85 | 86 | return categories; 87 | }, 88 | 89 | renderPlaceholder: function() { 90 | if ( this.state.filter.length > 0 ) { 91 | return ( 92 |
    No results found for “{ this.state.filter }”
    93 | ); 94 | } 95 | return ( 96 |
    Deliciousness is loading…
    97 | ); 98 | }, 99 | 100 | render: function() { 101 | let categories = this.getPosts(); 102 | let posts = []; 103 | 104 | this.setTitle(); 105 | 106 | for ( let cat of categories ) { 107 | if ( cat.posts.length === 0 ) { 108 | continue; 109 | } 110 | posts.push( 111 |
    112 |

    { cat.name }

    113 | 114 |
    115 | ); 116 | }; 117 | 118 | return ( 119 |
    120 | 121 | { posts.length ? 122 | posts : 123 | this.renderPlaceholder() 124 | } 125 | 126 |
    127 | ); 128 | } 129 | } ); 130 | 131 | export default CategoryList; 132 | -------------------------------------------------------------------------------- /js/components/posts/list.jsx: -------------------------------------------------------------------------------- 1 | // External dependencies 2 | import React from 'react'; 3 | 4 | // Internal dependencies 5 | import Post from './single'; 6 | 7 | let PostList = React.createClass( { 8 | propTypes: { 9 | posts: React.PropTypes.array.isRequired, 10 | }, 11 | 12 | render: function() { 13 | let posts = this.props.posts.map( function( post, i ) { 14 | return 15 | } ); 16 | 17 | return ( 18 |
      19 | { posts } 20 |
    21 | ); 22 | } 23 | } ); 24 | 25 | export default PostList; 26 | -------------------------------------------------------------------------------- /js/components/posts/single.jsx: -------------------------------------------------------------------------------- 1 | // External dependencies 2 | import React from 'react'; 3 | import classNames from 'classnames'; 4 | 5 | // Internal dependencies 6 | import ContentMixin from 'utils/content-mixin'; 7 | 8 | let Post = React.createClass( { 9 | mixins: [ ContentMixin ], 10 | 11 | render: function() { 12 | let post = this.props; 13 | 14 | let classes = classNames( { 15 | 'entry': true 16 | } ); 17 | 18 | return ( 19 |
  • 20 |

    21 | 22 |

    23 |
  • 24 | ); 25 | } 26 | } ); 27 | 28 | export default Post; 29 | -------------------------------------------------------------------------------- /js/components/posts/style.scss: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------------------------------- 3 | Lists of posts 4 | --------------------------------------------------------------*/ 5 | .placeholder { 6 | padding: 50px; 7 | text-align: center; 8 | } 9 | 10 | .post-list { 11 | text-align: center; 12 | } 13 | 14 | ol.site-main { 15 | margin: 0 auto 50px; 16 | padding: 0; 17 | max-width: 360px; 18 | list-style-type: upper-roman; 19 | text-align: left; 20 | 21 | li { 22 | font-weight: $font__weight-semibold; 23 | * { 24 | font-weight: normal; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /js/components/search/index.jsx: -------------------------------------------------------------------------------- 1 | // External dependencies 2 | import React from 'react'; 3 | import classNames from 'classnames'; 4 | 5 | let SearchForm = React.createClass( { 6 | getValue: function() { 7 | return this.refs.input.value; 8 | }, 9 | render: function() { 10 | return ( 11 |
    12 | 16 | 17 |
    18 | ); 19 | } 20 | } ); 21 | 22 | export default SearchForm; 23 | -------------------------------------------------------------------------------- /js/components/term/index.jsx: -------------------------------------------------------------------------------- 1 | // External dependencies 2 | import React from 'react'; 3 | 4 | // Internal dependencies 5 | import API from 'utils/api'; 6 | import PostsStore from '../../stores/posts-store'; 7 | import TermStore from '../../stores/term-store'; 8 | import PostList from '../posts/list'; 9 | // import Pagination from '../pagination'; 10 | 11 | /** 12 | * Method to retrieve state from Stores 13 | * 14 | * @param {string} term - Currently-displaying term 15 | * @return {object} - Current term and post listing 16 | */ 17 | function getState( term ) { 18 | return { 19 | data: TermStore.getTerm( term ), 20 | posts: PostsStore.getPosts(), 21 | }; 22 | } 23 | 24 | const Term = React.createClass( { 25 | propTypes: { 26 | page: React.PropTypes.number.isRequired, 27 | term: React.PropTypes.string.isRequired, 28 | taxonomy: React.PropTypes.string.isRequired, 29 | }, 30 | 31 | getInitialState: function() { 32 | return getState( this.props.term ); 33 | }, 34 | 35 | componentDidMount: function() { 36 | const filter = {}; 37 | if ( 'categories' === this.props.taxonomy ) { 38 | filter.category_name = this.props.term; 39 | } else { 40 | filter.tag = this.props.term; 41 | } 42 | 43 | TermStore.addChangeListener( this._onChange ); 44 | PostsStore.addChangeListener( this._onChange ); 45 | 46 | API.getTerm( this.props ); 47 | API.getPostsInTerm( filter ); 48 | }, 49 | 50 | componentDidUpdate: function( prevProps ) { 51 | if ( prevProps !== this.props ) { 52 | const filter = {}; 53 | if ( 'categories' === this.props.taxonomy ) { 54 | filter.category_name = this.props.term; 55 | } else { 56 | filter.tag = this.props.term; 57 | } 58 | API.getTerm( this.props ); 59 | API.getPostsInTerm( filter ); 60 | } 61 | }, 62 | 63 | componentWillUnmount: function() { 64 | TermStore.removeChangeListener( this._onChange ); 65 | PostsStore.removeChangeListener( this._onChange ); 66 | }, 67 | 68 | _onChange: function() { 69 | this.setState( getState( this.props.term ) ); 70 | }, 71 | 72 | renderEmpty: function() { 73 | return null; 74 | }, 75 | 76 | render: function() { 77 | const category = this.state.data; 78 | if ( 'undefined' === typeof category.name ) { 79 | return this.renderEmpty(); 80 | } 81 | 82 | return ( 83 |
    84 |
    85 |

    { category.name }

    86 | { category.description.length > 0 ? 87 |
    { category.description }
    : 88 | null 89 | } 90 |
    91 |
    92 | 93 |
    94 |
    95 | ); 96 | } 97 | } ); 98 | 99 | export default Term; 100 | -------------------------------------------------------------------------------- /js/components/term/style.scss: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | Term archive pages 3 | --------------------------------------------------------------*/ 4 | 5 | -------------------------------------------------------------------------------- /js/constants/constants.js: -------------------------------------------------------------------------------- 1 | import keyMirror from 'keymirror'; 2 | 3 | // This list describes all actions that are possible in this app 4 | // The actions themselves are handled in the respective *-actions.js 5 | // & *-store.js files 6 | // Add constants as you add functionality to the app, creating new 7 | // *-actions/*-store files if necessary. 8 | 9 | export default keyMirror( { 10 | // Post List actions 11 | REQUEST_POSTS: null, 12 | REQUEST_POSTS_SUCCESS: null, 13 | REQUEST_POSTS_ERROR: null, 14 | 15 | REQUEST_PAGINATION_LIMIT: null, 16 | 17 | // Category pages 18 | REQUEST_TERM_SUCCESS: null, 19 | 20 | // Post actions 21 | REQUEST_POST: null, 22 | REQUEST_POST_SUCCESS: null, 23 | REQUEST_POST_ERROR: null, 24 | 25 | // Nav actions 26 | REQUEST_NAV: null, 27 | REQUEST_NAV_SUCCESS: null, 28 | REQUEST_NAV_ERROR: null, 29 | } ); 30 | -------------------------------------------------------------------------------- /js/customize-preview.js: -------------------------------------------------------------------------------- 1 | /* global Anadama */ 2 | ( function( $, api ) { 3 | 4 | // Site title. 5 | api( 'blogname', function( value ) { 6 | value.bind( function( to ) { 7 | $( '.site-title a' ).text( to ); 8 | } ); 9 | } ); 10 | 11 | // Site tagline. 12 | api( 'blogdescription', function( value ) { 13 | value.bind( function( to ) { 14 | $( '.site-description' ).text( to ); 15 | } ); 16 | } ); 17 | 18 | /** 19 | * Override the handler for clicking links in preview to allow history.pushState() to do its thing. 20 | * 21 | * @param {jQuery.Event} event Event. 22 | */ 23 | api.Preview.prototype.handleLinkClick = function handleLinkClick( event ) { 24 | var link, isInternalJumpLink; 25 | link = $( event.target ); 26 | 27 | // No-op if the anchor is not a link. 28 | if ( _.isUndefined( link.attr( 'href' ) ) ) { 29 | return; 30 | } 31 | 32 | isInternalJumpLink = ( '#' === link.attr( 'href' ).substr( 0, 1 ) ); 33 | 34 | // Allow internal jump links to behave normally without preventing default. 35 | if ( isInternalJumpLink ) { 36 | return; 37 | } 38 | 39 | // If the link is not previewable, prevent the browser from navigating to it. 40 | if ( ! api.isLinkPreviewable( link[0] ) ) { 41 | wp.a11y.speak( api.settings.l10n.linkUnpreviewable ); 42 | event.preventDefault(); 43 | } 44 | }; 45 | 46 | // Override default behavior with no-op. 47 | api.navMenusPreview.onChangeNavMenuLocationsSetting = function() {}; 48 | 49 | api( 'nav_menu_locations[primary]', function previewNavMenuUpdates( navMenuLocationSetting ) { 50 | var assignedNavMenuId = navMenuLocationSetting.get(), refreshNavMenu, requestNavMenu; 51 | 52 | refreshNavMenu = function() { 53 | $( '#site-navigation' ).addClass( 'customize-partial-refreshing' ); 54 | requestNavMenu(); 55 | }; 56 | 57 | requestNavMenu = _.debounce( function() { 58 | Anadama.api.getMenu( '/menu-locations/primary/' ).done( function() { 59 | $( '#site-navigation' ).removeClass( 'customize-partial-refreshing' ); 60 | } ); 61 | }, api.settings.timeouts.selectiveRefresh ); 62 | 63 | navMenuLocationSetting.bind( function( menuId ) { 64 | assignedNavMenuId = menuId; 65 | refreshNavMenu(); 66 | } ); 67 | 68 | api.navMenusPreview.onChangeNavMenuSetting = function() { 69 | var navMenuSetting = this; 70 | if ( 'nav_menu[' + String( assignedNavMenuId ) + ']' === navMenuSetting.id ) { 71 | refreshNavMenu(); 72 | } 73 | }; 74 | api.navMenusPreview.onChangeNavMenuItemSetting = function( newItem, oldItem ) { 75 | if ( newItem && assignedNavMenuId === newItem.nav_menu_term_id || ! newItem && assignedNavMenuId === oldItem.nav_menu_term_id ) { 76 | refreshNavMenu(); 77 | } 78 | }; 79 | } ); 80 | 81 | } )( jQuery, wp.customize ); 82 | -------------------------------------------------------------------------------- /js/dispatcher/dispatcher.js: -------------------------------------------------------------------------------- 1 | var Dispatcher = require( 'flux' ).Dispatcher; 2 | var assign = require( 'object-assign' ); 3 | 4 | var AppDispatcher = assign( new Dispatcher(), { 5 | 6 | /** 7 | * A bridge function between the views and the dispatcher, marking the action 8 | * as a view action. Another variant here could be handleServerAction. 9 | * @param {object} action The data coming from the view. 10 | */ 11 | handleViewAction: function( action ) { 12 | console.log( 'Dispatch: ', action ); 13 | this.dispatch( { 14 | source: 'VIEW_ACTION', 15 | action: action 16 | } ); 17 | } 18 | 19 | } ); 20 | 21 | module.exports = AppDispatcher; 22 | -------------------------------------------------------------------------------- /js/index.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-multi-spaces */ 2 | /*global AnadamaSettings */ 3 | /** 4 | * Entry point for the app. 5 | * `page` is used to trigger the right controller for a route. 6 | * The controller renders the top-level component for a given page. 7 | */ 8 | 9 | // Load in the babel (es6) polyfill 10 | require( 'babel-polyfill' ); 11 | 12 | // External dependencies 13 | import page from 'page'; 14 | 15 | import API from 'utils/api'; 16 | 17 | // Internal dependencies 18 | import Controller from './components/controller'; 19 | 20 | const path = AnadamaSettings.path || '/'; 21 | page.base( path ); 22 | 23 | page( '', Controller.setup, Controller.navigation, Controller.posts ); 24 | page( 'page/:page', Controller.setup, Controller.navigation, Controller.posts ); 25 | page( 'category/:term', Controller.setup, Controller.navigation, Controller.term ); 26 | page( 'tag/:term', Controller.setup, Controller.navigation, Controller.term ); 27 | 28 | page( /^(?!wp-admin).*/, Controller.setup, Controller.navigation, Controller.post ); 29 | 30 | document.addEventListener( 'DOMContentLoaded', function() { 31 | page.start(); 32 | } ); 33 | 34 | module.exports = { 35 | page: page, 36 | api: API, 37 | }; 38 | -------------------------------------------------------------------------------- /js/stores/navigation-store.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import assign from 'object-assign'; 3 | import AppDispatcher from '../dispatcher/dispatcher'; 4 | import AppConstants from '../constants/constants'; 5 | 6 | var CHANGE_EVENT = 'change'; 7 | 8 | /** 9 | * Our working list of menu items, read-only 10 | * @type {array} 11 | * @protected 12 | */ 13 | var _menu = []; 14 | 15 | /** 16 | * Load this array into our menu 17 | * 18 | * @param {array} data - array of menu items, pulled from API 19 | */ 20 | function _loadMenu( data ) { 21 | _menu = data; 22 | } 23 | 24 | /** 25 | * Notify the user (via dev tools for now) that the menus couldn't load. 26 | */ 27 | function _notifyError( message, request ) { 28 | let error = `Menus failed to load. The endpoint returned: ${message}: ${request.responseJSON.message}`; 29 | if ( 'rest_no_route' === request.responseJSON.code ) { 30 | error += '. Please enable the `wp-api-menus` plugin to enable this endpoint.'; 31 | } 32 | console.warn( error ); 33 | } 34 | 35 | let NavigationStore = assign( {}, EventEmitter.prototype, { 36 | emitChange: function() { 37 | this.emit( CHANGE_EVENT ); 38 | }, 39 | 40 | addChangeListener: function( callback ) { 41 | this.on( CHANGE_EVENT, callback ); 42 | }, 43 | 44 | removeChangeListener: function( callback ) { 45 | this.removeListener( CHANGE_EVENT, callback ); 46 | }, 47 | 48 | /** 49 | * Get the menu 50 | * 51 | * @returns {array} 52 | */ 53 | getMenu: function() { 54 | return _menu; 55 | }, 56 | 57 | // Watch for store actions, and dispatch the above functions as necessary. 58 | dispatcherIndex: AppDispatcher.register( function( payload ) { 59 | var action = payload.action; // this is our action from handleViewAction 60 | 61 | switch ( action.actionType ) { 62 | case AppConstants.REQUEST_NAV_SUCCESS: 63 | _loadMenu( action.data ); 64 | break; 65 | case AppConstants.REQUEST_NAV_ERROR: 66 | _notifyError( action.message, action.data ); 67 | break; 68 | } 69 | 70 | NavigationStore.emitChange(); 71 | 72 | return true; 73 | } ) 74 | 75 | } ); 76 | 77 | export default NavigationStore; 78 | -------------------------------------------------------------------------------- /js/stores/posts-store.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import assign from 'object-assign'; 3 | import AppDispatcher from '../dispatcher/dispatcher'; 4 | import AppConstants from '../constants/constants'; 5 | 6 | import find from 'lodash/find'; 7 | import findIndex from 'lodash/findIndex'; 8 | 9 | var CHANGE_EVENT = 'change'; 10 | 11 | /** 12 | * Our working post list, read-only 13 | * @type {array} 14 | * @protected 15 | */ 16 | var _posts = []; 17 | 18 | /** 19 | * Our working posts-by-category list, read-only 20 | * @type {array} 21 | * @protected 22 | */ 23 | var _categories = []; 24 | 25 | /** 26 | * The total number of category pages 27 | * @type {int} 28 | * @protected 29 | */ 30 | var _total_cat_pages = 1; 31 | 32 | /** 33 | * Load this array into our posts list 34 | * 35 | * @param {array} data - array of posts, pulled from API 36 | */ 37 | function _loadPosts( data ) { 38 | // for each data.post, loadPost( id, post ) 39 | data.map( function( category ) { 40 | category.posts.map( function( post ) { 41 | _loadPost( post.id, post ); 42 | } ); 43 | } ); 44 | _categories = data; 45 | } 46 | 47 | /** 48 | * Load the number into the category total container 49 | * 50 | * @param {int} total - total category pages available, pulled from API 51 | */ 52 | function _loadPaginationLimit( total ) { 53 | _total_cat_pages = parseInt( total ); 54 | } 55 | 56 | /** 57 | * Load this array into our posts list 58 | * 59 | * @param {array} data - array of posts, pulled from API 60 | */ 61 | function _loadPost( id, data ) { 62 | var key = findIndex( _posts, function( _post ) { 63 | return parseInt( id ) === parseInt( _post.id ); 64 | } ); 65 | if ( -1 === key ) { 66 | _posts.push( data ); 67 | } 68 | } 69 | 70 | let PostsStore = assign( {}, EventEmitter.prototype, { 71 | emitChange: function() { 72 | this.emit( CHANGE_EVENT ); 73 | }, 74 | 75 | addChangeListener: function( callback ) { 76 | this.on( CHANGE_EVENT, callback ); 77 | }, 78 | 79 | removeChangeListener: function( callback ) { 80 | this.removeListener( CHANGE_EVENT, callback ); 81 | }, 82 | 83 | /** 84 | * Get the post list 85 | * 86 | * @returns {array} 87 | */ 88 | getPostsByCategory: function() { 89 | return _categories; 90 | }, 91 | 92 | /** 93 | * Get the number of available category pages 94 | * 95 | * @returns {array} 96 | */ 97 | getPaginationLimit: function() { 98 | return _total_cat_pages; 99 | }, 100 | 101 | /** 102 | * Get the post list 103 | * 104 | * @returns {array} 105 | */ 106 | getPosts: function() { 107 | return _posts; 108 | }, 109 | 110 | /** 111 | * Get the current post 112 | * 113 | * @returns {array} 114 | */ 115 | getPost: function( slug ) { 116 | var post = find( _posts, function( _post ) { 117 | return slug === _post.slug; 118 | } ); 119 | post = post || {}; 120 | return post; 121 | }, 122 | 123 | // Watch for store actions, and dispatch the above functions as necessary. 124 | dispatcherIndex: AppDispatcher.register( function( payload ) { 125 | var action = payload.action; // this is our action from handleViewAction 126 | 127 | switch ( action.actionType ) { 128 | case AppConstants.REQUEST_POSTS_SUCCESS: 129 | _loadPosts( action.data ); 130 | break; 131 | case AppConstants.REQUEST_POST_SUCCESS: 132 | _loadPost( action.id, action.data ); 133 | break; 134 | case AppConstants.REQUEST_PAGINATION_LIMIT: 135 | _loadPaginationLimit( action.data ); 136 | break; 137 | } 138 | 139 | PostsStore.emitChange(); 140 | 141 | return true; 142 | } ) 143 | 144 | } ); 145 | 146 | export default PostsStore; 147 | -------------------------------------------------------------------------------- /js/stores/term-store.js: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import assign from 'object-assign'; 3 | import AppDispatcher from '../dispatcher/dispatcher'; 4 | import AppConstants from '../constants/constants'; 5 | 6 | import find from 'lodash/find'; 7 | import findIndex from 'lodash/findIndex'; 8 | 9 | var CHANGE_EVENT = 'change'; 10 | 11 | /** 12 | * Our working term list, read-only 13 | * @type {array} 14 | * @protected 15 | */ 16 | var _terms = []; 17 | 18 | /** 19 | * Load this array into our posts list 20 | * 21 | * @param {array} data - array of posts, pulled from API 22 | */ 23 | function _loadTerm( id, data ) { 24 | var key = findIndex( _terms, function( _term ) { 25 | return parseInt( id ) === parseInt( _term.id ); 26 | } ); 27 | if ( -1 === key ) { 28 | _terms.push( data ); 29 | } 30 | } 31 | 32 | let TermStore = assign( {}, EventEmitter.prototype, { 33 | emitChange: function() { 34 | this.emit( CHANGE_EVENT ); 35 | }, 36 | 37 | addChangeListener: function( callback ) { 38 | this.on( CHANGE_EVENT, callback ); 39 | }, 40 | 41 | removeChangeListener: function( callback ) { 42 | this.removeListener( CHANGE_EVENT, callback ); 43 | }, 44 | 45 | /** 46 | * Get the current term 47 | * 48 | * @returns {array} 49 | */ 50 | getTerm: function( slug ) { 51 | var term = find( _terms, function( _term ) { 52 | return slug === _term.slug; 53 | } ); 54 | term = term || {}; 55 | return term; 56 | }, 57 | 58 | // Watch for store actions, and dispatch the above functions as necessary. 59 | dispatcherIndex: AppDispatcher.register( function( payload ) { 60 | var action = payload.action; // this is our action from handleViewAction 61 | 62 | switch ( action.actionType ) { 63 | case AppConstants.REQUEST_TERM_SUCCESS: 64 | _loadTerm( action.id, action.data ); 65 | break; 66 | } 67 | 68 | TermStore.emitChange(); 69 | 70 | return true; 71 | } ) 72 | 73 | } ); 74 | 75 | export default TermStore; 76 | -------------------------------------------------------------------------------- /js/utils/api.js: -------------------------------------------------------------------------------- 1 | /* global jQuery AnadamaSettings */ 2 | import first from 'lodash/first'; 3 | 4 | /** 5 | * Internal dependencies 6 | */ 7 | import PostActions from '../actions/post-actions'; 8 | import TermActions from '../actions/term-actions'; 9 | import NavActions from '../actions/nav-actions'; 10 | 11 | const _get = function( url, data ) { 12 | const cacheKey = url.replace( AnadamaSettings.URL.root, '' ) + JSON.stringify( data ); 13 | let postData = JSON.parse( localStorage.getItem( cacheKey ) ); 14 | if ( postData && AnadamaSettings.localStorageCache ) { 15 | let dfd = new jQuery.Deferred; 16 | return dfd.resolve( postData ); 17 | } 18 | 19 | return jQuery.ajax( { 20 | url: url, 21 | data: jQuery.extend( {}, data, { _wpnonce: AnadamaSettings.nonce } ), 22 | dataType: 'json', 23 | success: ( returnData ) => { 24 | if ( AnadamaSettings.localStorageCache ) { 25 | localStorage.setItem( cacheKey, JSON.stringify( returnData ) ); 26 | } 27 | } 28 | } ); 29 | }; 30 | 31 | const _getPagination = function( url, data, request ) { 32 | const cacheKey = url.replace( AnadamaSettings.URL.root, '' ) + JSON.stringify( data ) + '-pages'; 33 | if ( 'undefined' !== typeof request ) { 34 | PostActions.fetchPaginationLimit( request.getResponseHeader( 'X-WP-TotalPages' ) ); 35 | localStorage.setItem( cacheKey, request.getResponseHeader( 'X-WP-TotalPages' ) ); 36 | } else { 37 | PostActions.fetchPaginationLimit( localStorage.getItem( cacheKey ) ); 38 | } 39 | }; 40 | 41 | export default { 42 | 43 | // Get some categories, then for each category, get a few posts. 44 | // args: might have pagination. 45 | getPosts: function( args ) { 46 | const url = AnadamaSettings.URL.api + '/categories/'; 47 | var deferred = jQuery.Deferred(); 48 | // args.hide_empty = true; // disabled until the API fixes boolean params 49 | args.per_page = 10; 50 | 51 | jQuery.when( 52 | _get( url, args ) 53 | ).done( function( data, status, request ) { 54 | _getPagination( url, data, request ); // Set the page limit in PostsStore 55 | const requests = []; 56 | data.map( function( category ) { 57 | requests.push( _get( 58 | AnadamaSettings.URL.api + '/posts/', 59 | { 60 | per_page: 20, 61 | categories: category.id 62 | } 63 | ) ); 64 | } ); 65 | jQuery.when( ...requests ).done( function( ...results ) { 66 | // If `results` is just one request's results, make sure it's the expected format. 67 | if ( 'string' === typeof results[1] ) { 68 | results = [ results ]; 69 | } 70 | results.map( function( result, i ) { 71 | if ( 'success' === result[1] ) { 72 | // Successful response from API 73 | data[ i ].posts = result[ 0 ]; 74 | } else if ( 'string' === typeof result[1] ) { 75 | // Unsuccessful response from API 76 | data[ i ].posts = []; 77 | } else { 78 | // Pulled data from localStorage 79 | data[ i ].posts = result; 80 | } 81 | } ); 82 | 83 | deferred.resolve( results ); 84 | PostActions.fetch( data ); 85 | } ).fail( function( ...results ) { 86 | deferred.reject( ...results ); 87 | } ); 88 | } ); 89 | return deferred.promise(); 90 | }, 91 | 92 | // Get posts in a category 93 | getPostsInTerm: function( filter = {} ) { 94 | const url = AnadamaSettings.URL.api + '/posts/'; 95 | const args = { 96 | filter: filter, 97 | per_page: 20, 98 | }; 99 | 100 | return jQuery.when( 101 | _get( url, args ) 102 | ).done( function( data ) { 103 | // Fetch expects an array of arrays, thanks to the category setup above. 104 | PostActions.fetch( [ { posts: data } ] ); 105 | } ); 106 | }, 107 | 108 | // args: term, taxonomy 109 | getTerm: function( args ) { 110 | const url = `${AnadamaSettings.URL.api}/${args.taxonomy}/`; 111 | args = { 112 | search: args.term 113 | }; 114 | 115 | return jQuery.when( 116 | _get( url, args ) 117 | ).done( function( data ) { 118 | if ( data.constructor === Array ) { 119 | data = first( data ); 120 | } 121 | TermActions.fetch( data ); 122 | } ); 123 | }, 124 | 125 | // Get /{post_type}/?slug={slug} 126 | getPost: function( slug, type ) { 127 | const url = `${AnadamaSettings.URL.api}/${type}s/?slug=${slug}`; 128 | 129 | return jQuery.when( 130 | _get( url, {} ) 131 | ).done( function( data ) { 132 | if ( data.constructor === Array ) { 133 | data = first( data ); 134 | } 135 | PostActions.fetchSingle( data ); 136 | } ); 137 | }, 138 | 139 | // Get /wp-api-menus/v2/menu-locations/:location 140 | getMenu: function( path ) { 141 | const url = AnadamaSettings.URL.menuApi + path; 142 | 143 | return jQuery.when( 144 | _get( url, {} ) 145 | ).done( function( data ) { 146 | NavActions.fetch( data ); 147 | } ).fail( function( request, status, message ) { 148 | NavActions.fetchFailed( message, request ); 149 | } ); 150 | }, 151 | }; 152 | -------------------------------------------------------------------------------- /js/utils/content-mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | getTitle: function( data ) { 3 | return { __html: data.title.rendered }; 4 | }, 5 | 6 | getExcerpt: function( data ) { 7 | return { __html: data.excerpt.rendered }; 8 | }, 9 | 10 | getContent: function( data ) { 11 | return { __html: data.content.rendered }; 12 | }, 13 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Anadama", 3 | "version": "1.0.0", 4 | "description": "A react-based recipe theme for WordPress", 5 | "private": true, 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ryelle/Anadama-React" 9 | }, 10 | "scripts": { 11 | "build": "NODE_ENV=production gulp", 12 | "watch": "gulp watch", 13 | "dev": "gulp", 14 | "lint": "eslint js --ext=js,jsx" 15 | }, 16 | "license": "GPL-2.0+", 17 | "devDependencies": { 18 | "babel-core": "^6.13.2", 19 | "babel-eslint": "^6.1.2", 20 | "babel-loader": "^6.2.4", 21 | "babel-polyfill": "^6.13.0", 22 | "babel-preset-es2015": "^6.13.2", 23 | "babel-preset-react": "^6.11.1", 24 | "eslint": "^3.2.2", 25 | "eslint-loader": "^1.5.0", 26 | "eslint-plugin-react": "^6.0.0", 27 | "gulp": "^3.9.1", 28 | "gulp-autoprefixer": "^3.1.0", 29 | "gulp-sass": "^2.3.2", 30 | "gulp-util": "^3.0.7", 31 | "path": "^0.12.7", 32 | "webpack": "^1.13.1" 33 | }, 34 | "dependencies": { 35 | "classnames": "^2.2.5", 36 | "events": "^1.1.1", 37 | "flux": "^2.1.1", 38 | "keymirror": "^0.1.1", 39 | "lodash": "^4.14.1", 40 | "moment": "^2.14.1", 41 | "object-assign": "^4.1.0", 42 | "page": "^1.7.1", 43 | "react": "^15.3.0", 44 | "react-dom": "^15.3.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sass/_normalize.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | -webkit-text-size-adjust: 100%; 4 | -ms-text-size-adjust: 100%; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | article, 14 | aside, 15 | details, 16 | figcaption, 17 | figure, 18 | footer, 19 | header, 20 | main, 21 | menu, 22 | nav, 23 | section, 24 | summary { 25 | display: block; 26 | } 27 | 28 | audio, 29 | canvas, 30 | progress, 31 | video { 32 | display: inline-block; 33 | vertical-align: baseline; 34 | } 35 | 36 | audio:not([controls]) { 37 | display: none; 38 | height: 0; 39 | } 40 | 41 | [hidden], 42 | template { 43 | display: none; 44 | } 45 | 46 | a { 47 | background-color: transparent; 48 | } 49 | 50 | a:active, 51 | a:hover { 52 | outline: 0; 53 | } 54 | 55 | abbr[title] { 56 | border-bottom: 1px dotted; 57 | } 58 | 59 | b, 60 | strong { 61 | font-weight: bold; 62 | } 63 | 64 | dfn { 65 | font-style: italic; 66 | } 67 | 68 | h1 { 69 | font-size: 2em; 70 | margin: 0.67em 0; 71 | } 72 | 73 | mark { 74 | background: #ff0; 75 | color: #000; 76 | } 77 | 78 | small { 79 | font-size: 80%; 80 | } 81 | 82 | sub, 83 | sup { 84 | font-size: 75%; 85 | line-height: 0; 86 | position: relative; 87 | vertical-align: baseline; 88 | } 89 | 90 | sup { 91 | top: -0.5em; 92 | } 93 | 94 | sub { 95 | bottom: -0.25em; 96 | } 97 | 98 | img { 99 | border: 0; 100 | } 101 | 102 | svg:not(:root) { 103 | overflow: hidden; 104 | } 105 | 106 | figure { 107 | margin: 1em 40px; 108 | } 109 | 110 | hr { 111 | box-sizing: content-box; 112 | height: 0; 113 | } 114 | 115 | pre { 116 | overflow: auto; 117 | } 118 | 119 | code, 120 | kbd, 121 | pre, 122 | samp { 123 | font-family: monospace, monospace; 124 | font-size: 1em; 125 | } 126 | 127 | button, 128 | input, 129 | optgroup, 130 | select, 131 | textarea { 132 | color: inherit; 133 | font: inherit; 134 | margin: 0; 135 | } 136 | 137 | button { 138 | overflow: visible; 139 | } 140 | 141 | button, 142 | select { 143 | text-transform: none; 144 | } 145 | 146 | button, 147 | html input[type="button"], 148 | input[type="reset"], 149 | input[type="submit"] { 150 | -webkit-appearance: button; 151 | cursor: pointer; 152 | } 153 | 154 | button[disabled], 155 | html input[disabled] { 156 | cursor: default; 157 | } 158 | 159 | button::-moz-focus-inner, 160 | input::-moz-focus-inner { 161 | border: 0; 162 | padding: 0; 163 | } 164 | 165 | input { 166 | line-height: normal; 167 | } 168 | 169 | input[type="checkbox"], 170 | input[type="radio"] { 171 | box-sizing: border-box; 172 | padding: 0; 173 | } 174 | 175 | input[type="number"]::-webkit-inner-spin-button, 176 | input[type="number"]::-webkit-outer-spin-button { 177 | height: auto; 178 | } 179 | 180 | input[type="search"] { 181 | -webkit-appearance: textfield; 182 | box-sizing: content-box; 183 | } 184 | 185 | input[type="search"]::-webkit-search-cancel-button, 186 | input[type="search"]::-webkit-search-decoration { 187 | -webkit-appearance: none; 188 | } 189 | 190 | fieldset { 191 | border: 1px solid #c0c0c0; 192 | margin: 0 2px; 193 | padding: 0.35em 0.625em 0.75em; 194 | } 195 | 196 | legend { 197 | border: 0; 198 | padding: 0; 199 | } 200 | 201 | textarea { 202 | overflow: auto; 203 | } 204 | 205 | optgroup { 206 | font-weight: bold; 207 | } 208 | 209 | table { 210 | border-collapse: collapse; 211 | border-spacing: 0; 212 | } 213 | 214 | td, 215 | th { 216 | padding: 0; 217 | } 218 | -------------------------------------------------------------------------------- /sass/elements/_elements.scss: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | 5 | *, 6 | *:before, 7 | *:after { /* Inherit box-sizing to make it easier to change the property for components that leverage other behavior; see http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */ 8 | box-sizing: inherit; 9 | } 10 | 11 | body { 12 | background: $color__background-body; /* Fallback for when there is no custom background color defined. */ 13 | } 14 | 15 | blockquote:before, blockquote:after, 16 | q:before, q:after { 17 | content: ""; 18 | } 19 | 20 | blockquote, q { 21 | quotes: "" ""; 22 | } 23 | 24 | hr { 25 | background-color: $color__background-hr; 26 | border: 0; 27 | height: 1px; 28 | margin-bottom: 1.5em; 29 | } 30 | 31 | @import "lists"; 32 | 33 | img { 34 | height: auto; /* Make sure images are scaled correctly. */ 35 | max-width: 100%; /* Adhere to container width. */ 36 | } 37 | 38 | @import "tables"; -------------------------------------------------------------------------------- /sass/elements/_lists.scss: -------------------------------------------------------------------------------- 1 | ul, ol { 2 | margin: 0 0 1.5em 3em; 3 | } 4 | 5 | ul { 6 | list-style: disc; 7 | } 8 | 9 | ol { 10 | list-style: decimal; 11 | } 12 | 13 | li > ul, 14 | li > ol { 15 | margin-bottom: 0; 16 | margin-left: 1.5em; 17 | } 18 | 19 | dt { 20 | font-weight: $font__weight-semibold; 21 | } 22 | 23 | dd { 24 | margin: 0 1.5em 1.5em; 25 | } -------------------------------------------------------------------------------- /sass/elements/_tables.scss: -------------------------------------------------------------------------------- 1 | table { 2 | margin: 0 0 1.5em; 3 | width: 100%; 4 | } -------------------------------------------------------------------------------- /sass/forms/_buttons.scss: -------------------------------------------------------------------------------- 1 | button, 2 | input[type="button"], 3 | input[type="reset"], 4 | input[type="submit"] { 5 | border: none; 6 | border-radius: 3px; 7 | background: $color__background-button; 8 | color: $color__text-button; 9 | @include font-size( $font__size-main ); 10 | line-height: 1; 11 | padding: .6em 1em .4em; 12 | 13 | &:hover {} 14 | 15 | &:focus, 16 | &:active {} 17 | } 18 | -------------------------------------------------------------------------------- /sass/forms/_fields.scss: -------------------------------------------------------------------------------- 1 | input[type="text"], 2 | input[type="email"], 3 | input[type="url"], 4 | input[type="password"], 5 | input[type="search"], 6 | textarea { 7 | color: $color__text-input; 8 | @include font-size( 15 ); 9 | border: none; 10 | border-bottom: 2px solid $color__border-input; 11 | 12 | &:focus { 13 | outline: none; 14 | color: $color__text-input-focus; 15 | border-bottom-color: $color__border-input-focus; 16 | } 17 | } 18 | 19 | input[type="text"], 20 | input[type="email"], 21 | input[type="url"], 22 | input[type="password"], 23 | input[type="search"] { 24 | padding: 15px 40px 12px; 25 | line-height: 1; 26 | } 27 | 28 | textarea { 29 | padding-left: 3px; 30 | width: 100%; 31 | } -------------------------------------------------------------------------------- /sass/forms/_forms.scss: -------------------------------------------------------------------------------- 1 | 2 | @import "buttons"; 3 | @import "fields"; 4 | 5 | .search-form { 6 | .search-field { 7 | width: 100%; 8 | box-sizing: border-box; 9 | background: url( $icon__search ) no-repeat 15px center; 10 | background-size: 12px 12px; 11 | } 12 | } 13 | 14 | .search-submit { 15 | clip: rect(1px, 1px, 1px, 1px); 16 | position: absolute !important; 17 | height: 1px; 18 | width: 1px; 19 | overflow: hidden; 20 | } 21 | -------------------------------------------------------------------------------- /sass/media/_captions.scss: -------------------------------------------------------------------------------- 1 | .wp-caption { 2 | margin-bottom: 1.5em; 3 | max-width: 100%; 4 | 5 | img[class*="wp-image-"] { 6 | @include center-block; 7 | } 8 | 9 | .wp-caption-text { 10 | margin: 0.8075em 0; 11 | } 12 | } 13 | 14 | .wp-caption-text { 15 | text-align: center; 16 | } -------------------------------------------------------------------------------- /sass/media/_galleries.scss: -------------------------------------------------------------------------------- 1 | .gallery { 2 | margin-bottom: 1.5em; 3 | } 4 | 5 | .gallery-item { 6 | display: inline-block; 7 | text-align: center; 8 | vertical-align: top; 9 | width: 100%; 10 | } 11 | 12 | .gallery-columns-2 .gallery-item { 13 | max-width: 50%; 14 | } 15 | 16 | .gallery-columns-3 .gallery-item { 17 | max-width: 33.33%; 18 | } 19 | 20 | .gallery-columns-4 .gallery-item { 21 | max-width: 25%; 22 | } 23 | 24 | .gallery-columns-5 .gallery-item { 25 | max-width: 20%; 26 | } 27 | 28 | .gallery-columns-6 .gallery-item { 29 | max-width: 16.66%; 30 | } 31 | 32 | .gallery-columns-7 .gallery-item { 33 | max-width: 14.28%; 34 | } 35 | 36 | .gallery-columns-8 .gallery-item { 37 | max-width: 12.5%; 38 | } 39 | 40 | .gallery-columns-9 .gallery-item { 41 | max-width: 11.11%; 42 | } 43 | 44 | .gallery-caption { 45 | display: block; 46 | } -------------------------------------------------------------------------------- /sass/media/_media.scss: -------------------------------------------------------------------------------- 1 | .page-content .wp-smiley, 2 | .entry-content .wp-smiley, 3 | .comment-content .wp-smiley { 4 | border: none; 5 | margin-bottom: 0; 6 | margin-top: 0; 7 | padding: 0; 8 | } 9 | 10 | /* Make sure embeds and iframes fit their containers. */ 11 | embed, 12 | iframe, 13 | object { 14 | max-width: 100%; 15 | } 16 | 17 | /*-------------------------------------------------------------- 18 | 12.1 Captions 19 | --------------------------------------------------------------*/ 20 | @import "captions"; 21 | 22 | /*-------------------------------------------------------------- 23 | 12.2 Galleries 24 | --------------------------------------------------------------*/ 25 | @import "galleries"; -------------------------------------------------------------------------------- /sass/mixins/_mixins-master.scss: -------------------------------------------------------------------------------- 1 | // Rem output with px fallback 2 | @mixin font-size($sizeValue: 1) { 3 | font-size: $sizeValue + px; 4 | font-size: ($sizeValue / 16) + rem; 5 | } 6 | 7 | // Center block 8 | @mixin center-block { 9 | display: block; 10 | margin-left: auto; 11 | margin-right: auto; 12 | } 13 | 14 | // Clearfix 15 | @mixin clearfix() { 16 | content: ""; 17 | display: table; 18 | } 19 | 20 | // Clear after (not all clearfix need this also) 21 | @mixin clearfix-after() { 22 | clear: both; 23 | } 24 | -------------------------------------------------------------------------------- /sass/modules/_accessibility.scss: -------------------------------------------------------------------------------- 1 | /* Text meant only for screen readers. */ 2 | .screen-reader-text { 3 | clip: rect(1px, 1px, 1px, 1px); 4 | position: absolute !important; 5 | height: 1px; 6 | width: 1px; 7 | overflow: hidden; 8 | 9 | &:hover, 10 | &:active, 11 | &:focus { 12 | background-color: $color__background-screen; 13 | border-radius: 3px; 14 | box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6); 15 | clip: auto !important; 16 | color: $color__text-screen; 17 | display: block; 18 | @include font-size( 14 ); 19 | font-weight: $font__weight-semibold; 20 | height: auto; 21 | left: 5px; 22 | line-height: normal; 23 | padding: 15px 23px 14px; 24 | text-decoration: none; 25 | top: 5px; 26 | width: auto; 27 | z-index: 100000; /* Above WP toolbar. */ 28 | } 29 | } -------------------------------------------------------------------------------- /sass/modules/_alignments.scss: -------------------------------------------------------------------------------- 1 | .alignleft { 2 | display: inline; 3 | float: left; 4 | margin-right: 1.5em; 5 | } 6 | 7 | .alignright { 8 | display: inline; 9 | float: right; 10 | margin-left: 1.5em; 11 | } 12 | 13 | .aligncenter { 14 | @include center-block; 15 | } -------------------------------------------------------------------------------- /sass/modules/_clearings.scss: -------------------------------------------------------------------------------- 1 | .clear:before, 2 | .clear:after, 3 | .entry-content:before, 4 | .entry-content:after, 5 | .comment-content:before, 6 | .comment-content:after, 7 | .site-header:before, 8 | .site-header:after, 9 | .site-content:before, 10 | .site-content:after, 11 | .site-footer:before, 12 | .site-footer:after { 13 | @include clearfix; 14 | } 15 | 16 | .clear:after, 17 | .entry-content:after, 18 | .comment-content:after, 19 | .site-header:after, 20 | .site-content:after, 21 | .site-footer:after { 22 | @include clearfix-after; 23 | } -------------------------------------------------------------------------------- /sass/modules/_infinite-scroll.scss: -------------------------------------------------------------------------------- 1 | /* Globally hidden elements when Infinite Scroll is supported and in use. */ 2 | .infinite-scroll .posts-navigation, /* Older / Newer Posts Navigation (always hidden) */ 3 | .infinite-scroll.neverending .site-footer { /* Theme Footer (when set to scrolling) */ 4 | display: none; 5 | } 6 | 7 | /* When Infinite Scroll has reached its end we need to re-display elements that were hidden (via .neverending) before. */ 8 | .infinity-end.neverending .site-footer { 9 | display: block; 10 | } -------------------------------------------------------------------------------- /sass/navigation/_links.scss: -------------------------------------------------------------------------------- 1 | a { 2 | color: $color__link; 3 | 4 | &:visited { 5 | color: $color__link-visited; 6 | } 7 | &:hover, 8 | &:focus, 9 | &:active { 10 | color: $color__link-hover; 11 | } 12 | &:focus { 13 | outline: thin dotted; 14 | } 15 | &:hover, 16 | &:active { 17 | outline: 0; 18 | } 19 | 20 | .post-navigation & { 21 | font-weight: $font__weight-semibold; 22 | text-decoration: none; 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /sass/navigation/_navigation.scss: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | 5.1 Links 3 | --------------------------------------------------------------*/ 4 | @import "links"; 5 | -------------------------------------------------------------------------------- /sass/site/_site.scss: -------------------------------------------------------------------------------- 1 | 2 | @import "structure"; 3 | 4 | @import "primary/header"; 5 | @import "primary/footer"; 6 | 7 | @import "../../js/components/posts/style"; 8 | @import "../../js/components/post/style"; 9 | @import "../../js/components/navigation/style"; 10 | @import "../../js/components/pagination/style"; 11 | -------------------------------------------------------------------------------- /sass/site/_structure.scss: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | Site structure 3 | --------------------------------------------------------------*/ 4 | 5 | .site-branding, 6 | .search-form { 7 | margin: 0 auto; 8 | max-width: $size__site-main; 9 | } 10 | 11 | .search-form { 12 | margin-bottom: 75px; 13 | } 14 | -------------------------------------------------------------------------------- /sass/site/primary/_footer.scss: -------------------------------------------------------------------------------- 1 | 2 | .site-footer { 3 | padding: 50px 0; 4 | background-color: $beige; 5 | @include font-size( 16 ); 6 | font-weight: $font__weight-semibold; 7 | text-transform: uppercase; 8 | text-align: center; 9 | 10 | a { 11 | text-decoration: none; 12 | 13 | &:hover, 14 | &:active, 15 | &:focus { 16 | text-decoration: underline; 17 | } 18 | } 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /sass/site/primary/_header.scss: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------- 2 | Header 3 | --------------------------------------------------------------*/ 4 | .site-header { 5 | margin-bottom: 40px; 6 | padding-bottom: 40px; 7 | color: white; 8 | background: $color__background-header; 9 | text-align: center; 10 | } 11 | 12 | .site-title a, 13 | .site-logo-link { 14 | &:hover, 15 | &:active, 16 | &:focus { 17 | outline: none; 18 | border-bottom: 1px solid white; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sass/style.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme Name: Anadama 3 | Theme URI: http://underscores.me/ 4 | Author: Kelly Dwan & Mel Choyce 5 | Author URI: http://themes.redradar.net 6 | Description: A react-based recipe theme. 7 | Version: 1.0.0 8 | License: GNU General Public License v2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | Text Domain: anadama 11 | Tags: 12 | 13 | This theme, like WordPress, is licensed under the GPL. 14 | Use it to make something cool, have fun, and share what you've learned with others. 15 | 16 | Anadama is based on Underscores http://underscores.me/, (C) 2012-2015 Automattic, Inc. 17 | Underscores is distributed under the terms of the GNU GPL v2 or later. 18 | 19 | Normalizing styles have been helped along thanks to the fine work of 20 | Nicolas Gallagher and Jonathan Neal http://necolas.github.com/normalize.css/ 21 | */ 22 | 23 | @import "variables-site/variables-site"; 24 | @import "mixins/mixins-master"; 25 | 26 | /*-------------------------------------------------------------- 27 | 1.0 Normalize 28 | --------------------------------------------------------------*/ 29 | @import "normalize"; 30 | 31 | /*-------------------------------------------------------------- 32 | 2.0 Typography 33 | --------------------------------------------------------------*/ 34 | @import "typography/typography"; 35 | 36 | /*-------------------------------------------------------------- 37 | 3.0 Elements 38 | --------------------------------------------------------------*/ 39 | @import "elements/elements"; 40 | 41 | /*-------------------------------------------------------------- 42 | 4.0 Forms 43 | --------------------------------------------------------------*/ 44 | @import "forms/forms"; 45 | 46 | /*-------------------------------------------------------------- 47 | 5.0 Navigation 48 | --------------------------------------------------------------*/ 49 | @import "navigation/navigation"; 50 | 51 | /*-------------------------------------------------------------- 52 | 6.0 Accessibility 53 | --------------------------------------------------------------*/ 54 | @import "modules/accessibility"; 55 | 56 | /*-------------------------------------------------------------- 57 | 7.0 Alignments 58 | --------------------------------------------------------------*/ 59 | @import "modules/alignments"; 60 | 61 | /*-------------------------------------------------------------- 62 | 8.0 Clearings 63 | --------------------------------------------------------------*/ 64 | @import "modules/clearings"; 65 | 66 | /*-------------------------------------------------------------- 67 | 10.0 Content 68 | --------------------------------------------------------------*/ 69 | @import "site/site"; 70 | 71 | /*-------------------------------------------------------------- 72 | 11.0 Infinite scroll 73 | --------------------------------------------------------------*/ 74 | @import "modules/infinite-scroll"; 75 | 76 | /*-------------------------------------------------------------- 77 | 12.0 Media 78 | --------------------------------------------------------------*/ 79 | @import "media/media"; 80 | -------------------------------------------------------------------------------- /sass/typography/_copy.scss: -------------------------------------------------------------------------------- 1 | p { 2 | margin-bottom: 1.5em; 3 | } 4 | 5 | b, strong { 6 | font-weight: $font__weight-semibold; 7 | } 8 | 9 | dfn, cite, em, i { 10 | font-style: italic; 11 | } 12 | 13 | blockquote { 14 | margin: 0 1.5em; 15 | } 16 | 17 | address { 18 | margin: 0 0 1.5em; 19 | } 20 | 21 | pre { 22 | background: $color__background-pre; 23 | font-family: $font__pre; 24 | line-height: $font__line-height-pre; 25 | margin-bottom: 1.6em; 26 | max-width: 100%; 27 | overflow: auto; 28 | padding: 1.6em; 29 | } 30 | 31 | code, kbd, tt, var { 32 | font-family: $font__code; 33 | } 34 | 35 | abbr, acronym { 36 | border-bottom: 1px dotted $color__border-abbr; 37 | cursor: help; 38 | } 39 | 40 | mark, ins { 41 | background: $color__background-ins; 42 | text-decoration: none; 43 | } 44 | 45 | small { 46 | font-size: 75%; 47 | } 48 | 49 | big { 50 | font-size: 125%; 51 | } 52 | 53 | .entry-meta, 54 | .entry-footer { 55 | @include font-size( $font__size-meta ); 56 | } -------------------------------------------------------------------------------- /sass/typography/_headings.scss: -------------------------------------------------------------------------------- 1 | 2 | h1, h2 { 3 | font-family: $font__header; 4 | } 5 | 6 | h3, h4, h5, h6 { 7 | font-family: $font__main; 8 | font-weight: $font__weight-semibold; 9 | } 10 | 11 | .site-title { 12 | margin: 25px 0; 13 | @include font-size( 36 ); 14 | 15 | a { text-decoration: none; } 16 | } 17 | 18 | .site-description { 19 | margin: 0; 20 | } 21 | 22 | .section-title { 23 | margin-bottom: 25px; 24 | @include font-size( 24 ); 25 | } 26 | 27 | .page-header { 28 | padding: 0 0 10px; 29 | margin-bottom: 30px; 30 | text-align: center; 31 | border-bottom: 2px solid $color__border-input; 32 | } 33 | 34 | .page-title { 35 | font-family: $font__main; 36 | font-weight: $font__weight-semibold; 37 | @include font-size( $font__size-header ); 38 | } 39 | 40 | .entry-title { 41 | padding: 0 0 10px; 42 | margin-bottom: 30px; 43 | font-family: $font__main; 44 | font-weight: $font__weight-semibold; 45 | @include font-size( $font__size-header ); 46 | text-align: center; 47 | border-bottom: 2px solid $color__border-input; 48 | } 49 | 50 | .post-list .entry-title { 51 | padding: 0; 52 | margin: 0; 53 | font-weight: normal; 54 | @include font-size( $font__size-main ); 55 | text-align: left; 56 | border-bottom: none; 57 | } 58 | -------------------------------------------------------------------------------- /sass/typography/_typography.scss: -------------------------------------------------------------------------------- 1 | body, 2 | button, 3 | input, 4 | select, 5 | textarea { 6 | color: $color__text-main; 7 | font-family: $font__main; 8 | @include font-size( $font__size-main ); 9 | line-height: $font__line-height-body; 10 | } 11 | 12 | * { 13 | -ms-word-wrap: break-word; 14 | word-wrap: break-word; 15 | } 16 | 17 | @import "headings"; 18 | 19 | @import "copy"; 20 | -------------------------------------------------------------------------------- /sass/variables-site/_colors.scss: -------------------------------------------------------------------------------- 1 | 2 | // Base Colors 3 | $dark-brown: #332e29; 4 | $mid-brown: #8e8579; 5 | $light-brown: #e3e0dd; 6 | $beige: #eeedeb; 7 | 8 | // In-context colors 9 | 10 | $color__background-body: #fff; 11 | $color__background-header: $mid-brown; 12 | $color__background-screen: $beige; 13 | $color__background-hr: $light-brown; 14 | $color__background-button: $mid-brown; 15 | $color__background-pre: $beige; 16 | $color__background-ins: $beige; 17 | $color__background-overlay: $dark-brown; 18 | 19 | $color__text-main: $mid-brown; 20 | 21 | $color__text-screen: #333; 22 | $color__text-button: #fff; 23 | $color__text-input: $color__text-main; 24 | $color__text-input-focus: $color__text-main; 25 | 26 | $color__link: inherit; 27 | $color__link-visited: inherit; 28 | $color__link-hover: inherit; 29 | 30 | $color__border-input: $mid-brown; 31 | $color__border-input-focus: $dark-brown; 32 | $color__border-abbr: $light-brown; 33 | $color__border-post: $light-brown; 34 | -------------------------------------------------------------------------------- /sass/variables-site/_data-uris.scss: -------------------------------------------------------------------------------- 1 | 2 | $icon__search: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTQzOUM4NzM5QjY2MTFFNEI3RUJFQUEyRDQxRTAwNjIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTQzOUM4NzQ5QjY2MTFFNEI3RUJFQUEyRDQxRTAwNjIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1NDM5Qzg3MTlCNjYxMUU0QjdFQkVBQTJENDFFMDA2MiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1NDM5Qzg3MjlCNjYxMUU0QjdFQkVBQTJENDFFMDA2MiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PjT7oYcAAAFASURBVHjatFXtjYMwDE0RC7BCV4ARuBFYAUbodYBK3J/7DyswQhmhWYEVusIF6UWy3GAbKfekp9A0yfNH7Fx+H3eXQBfYglfMbYEruDgjSva7DpwwcuxCPegDB4wiCnb4E+Nu7XdgE3gBG8xtbK1JIG6oAmcc9sMs9JhrsKayiESBiRy+u/4W9ryxJopMmkDHwmIFDVcnCbT4nhXLU57M+G4tAqs7j9UicCVJPAtPrrB6Tf8FBRLlLHc6gZpU+aGAGkcBav6oQI97bUWFPU7qTQX+9EjUeEJgxJ5V88CR6u1JVUuWT8T6zdIqdg++iMgr8MYSX2PuRQ53xCi1XUeR2K5HpcA2ItSTSIjvgUe3lB6chcVcFCkPLFyMr9agieSo5IE0vY+c5GoVhyJlxraTDFfuZsc9yeqBS9yi4U+AAQDLgFDcZ/ylZgAAAABJRU5ErkJggg=="; 3 | 4 | $icon__arrow: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NkYxRjg5OTQ5QkM3MTFFNEI3RUJFQUEyRDQxRTAwNjIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NkYxRjg5OTU5QkM3MTFFNEI3RUJFQUEyRDQxRTAwNjIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1NDM5Qzg3NTlCNjYxMUU0QjdFQkVBQTJENDFFMDA2MiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1NDM5Qzg3NjlCNjYxMUU0QjdFQkVBQTJENDFFMDA2MiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PodmITcAAACfSURBVHjaYvj//z8DHnz3PwIo4VPLxEAlMGrQABjkAsShJOpVQtEDTANpSGnFmMh0JIgkVw5LR8ZItuyG2kQIIKsD+UYQZsNMJJvvItmOzUW7kcTOQF3HgOyNmVgUoBuETQ0DukEgvApJ4W40g1bhMgSbQehOxwbuYsvAuHLzblIMwWeQINT5RBmCzyB0w95hSWNEG4RsmDEBdQwAAQYAzBxeeZsMjuwAAAAASUVORK5CYII="; 5 | -------------------------------------------------------------------------------- /sass/variables-site/_structure.scss: -------------------------------------------------------------------------------- 1 | 2 | $size__site-main: 660px; 3 | $size__site-post: 1000px; 4 | -------------------------------------------------------------------------------- /sass/variables-site/_typography.scss: -------------------------------------------------------------------------------- 1 | 2 | $font__main: "Source Code Pro", monospace; 3 | $font__header: "Source Serif Pro", serif; 4 | $font__code: "Source Code Pro", Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; 5 | $font__pre: "Source Code Pro", "Courier 10 Pitch", Courier, monospace; 6 | 7 | $font__size-header: 30; 8 | $font__size-main: 18; 9 | $font__size-recipe-meta: 16; 10 | $font__size-meta: 14; 11 | 12 | $font__line-height-body: 1.57142857143; 13 | $font__line-height-pre: 1.57142857143; 14 | 15 | $font__weight-normal: 400; 16 | $font__weight-semibold: 600; 17 | $font__weight-bold: 700; 18 | -------------------------------------------------------------------------------- /sass/variables-site/_variables-site.scss: -------------------------------------------------------------------------------- 1 | @import "colors"; 2 | @import "typography"; 3 | @import "structure"; 4 | @import "data-uris"; 5 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryelle/Anadama-React/21c8f550bc3ea6bbb7407891bf06845316b0ebf6/screenshot.png -------------------------------------------------------------------------------- /sidebar.php: -------------------------------------------------------------------------------- 1 |