├── .gitignore ├── HISTORY.md ├── LICENSE ├── README.md ├── bower.json ├── gulpfile.js ├── less ├── components.less ├── components │ ├── Alertbar.less │ ├── Button.less │ ├── Group.less │ ├── List.less │ ├── NavigationBar.less │ ├── Popup.less │ ├── SearchField.less │ ├── SegmentedControl.less │ ├── Switch.less │ ├── TabsNavigator.less │ ├── keypad.less │ └── passcode.less ├── core.less ├── core │ ├── animations.less │ ├── forms.less │ ├── grid.less │ ├── normalize.less │ ├── scaffolding.less │ └── type.less ├── mixins.less ├── mixins │ ├── border-radius.less │ ├── buttons.less │ ├── clearfix.less │ ├── flex.less │ ├── gradients.less │ ├── image.less │ ├── resize.less │ ├── retina-borders.less │ ├── size.less │ ├── text.less │ └── vendor-prefixes.less ├── touchstone.less ├── utils.less ├── utils │ ├── align.less │ ├── display.less │ ├── layout.less │ ├── position.less │ ├── spacing.less │ └── text.less └── variables.less ├── lib ├── components │ ├── Alertbar.js │ ├── Button.js │ ├── ButtonGroup.js │ ├── FieldControl.js │ ├── FieldLabel.js │ ├── Group.js │ ├── GroupBody.js │ ├── GroupFooter.js │ ├── GroupHeader.js │ ├── Input.js │ ├── Item.js │ ├── ItemContent.js │ ├── ItemInner.js │ ├── ItemMedia.js │ ├── ItemNote.js │ ├── ItemSubTitle.js │ ├── ItemTitle.js │ ├── LabelInput.js │ ├── LabelSelect.js │ ├── LabelTextarea.js │ ├── ListHeader.js │ ├── NavigationBar.js │ ├── Popup.js │ ├── PopupIcon.js │ ├── RadioList.js │ ├── SearchField.js │ ├── SegmentedControl.js │ ├── Switch.js │ ├── Tabs.js │ ├── Textarea.js │ └── index.js └── index.js ├── package.json └── src ├── components ├── Alertbar.js ├── Button.js ├── ButtonGroup.js ├── FieldControl.js ├── FieldLabel.js ├── Group.js ├── GroupBody.js ├── GroupFooter.js ├── GroupHeader.js ├── Input.js ├── Item.js ├── ItemContent.js ├── ItemInner.js ├── ItemMedia.js ├── ItemNote.js ├── ItemSubTitle.js ├── ItemTitle.js ├── LabelInput.js ├── LabelSelect.js ├── LabelTextarea.js ├── ListHeader.js ├── NavigationBar.js ├── Popup.js ├── PopupIcon.js ├── RadioList.js ├── SearchField.js ├── SegmentedControl.js ├── Switch.js ├── Tabs.js ├── Textarea.js └── index.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # TouchstoneJS UI 2 | 3 | Please be aware that TouchstoneJS UI is still under heavy development; the components are liable to change and possibly break things. 4 | 5 | ## v0.1.0 / 2015-10-07 6 | 7 | * First release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 TouchstoneJS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TouchstoneJS UI 2 | 3 | React.js UI components for the TouchstoneJS platform http://touchstonejs.io 4 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "touchstonejs", 3 | "version": "0.1.0", 4 | "homepage": "https://github.com/touchstonejs/touchstonejs", 5 | "authors": [ 6 | "Jed Watson" 7 | ], 8 | "description": "React.js UI components for the TouchstoneJS platform http://touchstonejs.io", 9 | "moduleType": [ 10 | "node" 11 | ], 12 | "keywords": [ 13 | "touchstone", 14 | "touchstonejs", 15 | "ui", 16 | "react", 17 | "react-component", 18 | "mobile", 19 | "hybrid" 20 | ], 21 | "license": "MIT", 22 | "ignore": [ 23 | ".editorconfig", 24 | ".gitignore", 25 | "package.json", 26 | "site" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var babel = require('gulp-babel'); 2 | var babelify = require('babelify'); 3 | var browserify = require('browserify'); 4 | var gutil = require('gutil'); 5 | var bump = require('gulp-bump'); 6 | var connect = require('gulp-connect'); 7 | var del = require('del'); 8 | var deploy = require('gulp-gh-pages'); 9 | var git = require('gulp-git'); 10 | var gulp = require('gulp'); 11 | var less = require('gulp-less'); 12 | var source = require('vinyl-source-stream'); 13 | 14 | // Build/Clean/Watch lib 15 | gulp.task('build:lib', function () { 16 | return gulp.src('src/**/*') 17 | .pipe(babel({ plugins: [require('babel-plugin-object-assign')] })) 18 | .pipe(gulp.dest('lib')); 19 | }); 20 | 21 | gulp.task('clean:lib', function (done) { del(['lib'], done); }); 22 | gulp.task('watch:lib', ['build:lib'], function () { 23 | gulp.watch('src/**/*', ['build:lib']); 24 | }); 25 | 26 | // Publish 27 | gulp.task('publish:npm', function (done) { 28 | require('child_process') 29 | .spawn('npm', ['publish'], { stdio: 'inherit' }) 30 | .on('close', done); 31 | }); 32 | 33 | gulp.task('publish:tag', function (done) { 34 | var pkg = require('./package.json'); 35 | var v = 'v' + pkg.version; 36 | var message = 'Release ' + v; 37 | 38 | git.tag(v, message, function (err) { 39 | if (err) throw err; 40 | 41 | git.push('origin', v, done); 42 | }); 43 | }); 44 | 45 | gulp.task('release', ['publish:tag', 'publish:npm']); 46 | 47 | // Version 48 | function _bump (type) { 49 | return gulp.src(['./package.json', './bower.json']) 50 | .pipe(bump({ type: type })) 51 | .pipe(gulp.dest('./')); 52 | } 53 | 54 | gulp.task('bump', _bump.bind(null, 'patch')); 55 | gulp.task('bump:minor', _bump.bind(null, 'minor')); 56 | gulp.task('bump:major', _bump.bind(null, 'major')); 57 | -------------------------------------------------------------------------------- /less/components.less: -------------------------------------------------------------------------------- 1 | // 2 | // Components 3 | // ============================== 4 | 5 | @import "components/keypad"; 6 | @import "components/passcode"; 7 | 8 | @import "components/Alertbar"; 9 | @import "components/Button"; 10 | @import "components/Group"; 11 | @import "components/List"; 12 | @import "components/NavigationBar"; 13 | @import "components/Popup"; 14 | @import "components/SearchField"; 15 | @import "components/SegmentedControl"; 16 | @import "components/Switch"; 17 | @import "components/TabsNavigator"; 18 | -------------------------------------------------------------------------------- /less/components/Alertbar.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alertbar 3 | // ============================== 4 | 5 | 6 | // base 7 | 8 | .Alertbar { 9 | .translateZ(0); // stop re-paint when introduced into the DOM 10 | .transition( background-color 160ms ); // animate variant if state changed while visible 11 | color: white; 12 | font-size: @font-size-sm; 13 | left: 0; 14 | line-height: 1.1; 15 | padding: @gutter-sm; 16 | position: absolute; 17 | text-align: center; 18 | top: 0; 19 | width: 100%; 20 | z-index: @zindex-alertbar; 21 | } 22 | 23 | 24 | // overlay translucent for pulse 25 | 26 | .Alertbar--pulse::after { 27 | .animation( pulse 2s linear infinite ); 28 | background-color: black; 29 | bottom: 0; 30 | content: ""; 31 | left: 0; 32 | opacity: 0; 33 | position: absolute; 34 | right: 0; 35 | top: 0; 36 | z-index: 1; 37 | } 38 | 39 | 40 | // variants 41 | 42 | .Alertbar--danger { background-color: @app-danger; } 43 | .Alertbar--default { background-color: @gray-light; } 44 | .Alertbar--info { background-color: @app-info; } 45 | .Alertbar--primary { background-color: @app-primary; } 46 | .Alertbar--success { background-color: @app-success; } 47 | .Alertbar--warning { background-color: @app-warning; } 48 | 49 | 50 | // sit the text above the pulsing background 51 | 52 | .Alertbar__inner { 53 | position: relative; 54 | z-index: 2; 55 | } 56 | 57 | 58 | 59 | 60 | // Animation 61 | // ------------------------------ 62 | 63 | // apply animation 64 | 65 | .Alertbar-enter { 66 | .animation( alertbarEnter 240ms @view-transition-timing-function ); 67 | } 68 | .Alertbar-leave { 69 | .animation( alertbarLeave 240ms @view-transition-timing-function ); 70 | } 71 | 72 | // slide up 73 | 74 | @-webkit-keyframes alertbarEnter { 75 | from {-webkit-transform: translate3d(0, -100%, 0); opacity: .5; } 76 | to { -webkit-transform: none; opacity: 1; } 77 | } 78 | @keyframes alertbarEnter { 79 | from {transform: translate3d(0, -100%, 0); opacity: .5; } 80 | to { transform: none; opacity: 1; } 81 | } 82 | 83 | // slide down 84 | 85 | @-webkit-keyframes alertbarLeave { 86 | from { -webkit-transform: none; opacity: 1; } 87 | to { -webkit-transform: translate3d(0, -100%, 0); opacity: .5; } 88 | } 89 | @keyframes alertbarLeave { 90 | from { transform: none; opacity: 1; } 91 | to {transform: translate3d(0, -100%, 0); opacity: .5; } 92 | } 93 | -------------------------------------------------------------------------------- /less/components/Button.less: -------------------------------------------------------------------------------- 1 | // 2 | // Button 3 | // ============================== 4 | 5 | 6 | 7 | 8 | // Base 9 | // ------------------------------ 10 | 11 | .Button { 12 | .retina-1px-border-top-and-bottom( rgba(0,0,0,0.17) ); 13 | .transition( color 160ms ); // animate variation if disabled state changed while visible 14 | background-color: white; 15 | border: none; 16 | color: @text-color; 17 | display: block; 18 | font-weight: normal; 19 | height: @item-height; 20 | line-height: @item-height; 21 | margin-bottom: @gutter-lg; 22 | outline: none; 23 | padding: 0 @padding-base-horizontal; 24 | position: relative; 25 | text-align: center; 26 | text-decoration: none; 27 | width: 100%; 28 | -webkit-appearance: none; 29 | 30 | // stack panel buttons closer to one another when in a row 31 | & + & { 32 | margin-top: -(@gutter-lg / 2); 33 | } 34 | 35 | // add active state 36 | &.Tappable-active { 37 | background-color: @item-bg-tap; 38 | } 39 | 40 | // add disabled state 41 | &[disabled] { 42 | color: @button-disabled-color; 43 | pointer-events: none; 44 | } 45 | 46 | // icons in buttons 47 | & > .icon-sm, 48 | & > .icon-md, 49 | & > .icon-lg { 50 | display: inline-block; 51 | vertical-align: middle; 52 | } 53 | 54 | } 55 | 56 | 57 | // variants 58 | 59 | .Button--danger { color: @app-danger; } 60 | .Button--default { color: @text-color; } 61 | .Button--info { color: @app-info; } 62 | .Button--primary { color: @app-primary; font-weight: @font-weight-bold; } 63 | .Button--success { color: @app-success; } 64 | .Button--warning { color: @app-warning; } 65 | 66 | 67 | 68 | 69 | // Panel Button Groups 70 | // ------------------------------ 71 | 72 | // stack buttons horizontally 73 | 74 | .ButtonGroup { 75 | .display-flex(); 76 | .retina-1px-border-top-and-bottom( rgba(0,0,0,0.17) ); // move the button borders to the group 77 | margin-bottom: @gutter-lg; 78 | 79 | > .Button { 80 | border-left: 1px solid rgba(0,0,0,0.17); 81 | margin: 0; 82 | 83 | &:first-child { 84 | border-left: none; 85 | } 86 | 87 | // remove the borders - they're handled by the group 88 | &::before, 89 | &::after { 90 | display: none; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /less/components/Group.less: -------------------------------------------------------------------------------- 1 | // 2 | // Group 3 | // ============================== 4 | 5 | // base 6 | 7 | .Group { 8 | .retina-1px-border-top-and-bottom( rgba(0,0,0,0.17) ); 9 | background-color: @group-bg; 10 | color: @group-color; 11 | display: block; 12 | margin-bottom: @gutter-lg; 13 | 14 | // provide a nice gutter between the first group and the header 15 | &:first-child { 16 | margin-top: @gutter-lg; 17 | } 18 | 19 | // when you want the whole group to be tappable 20 | &.Tappable-active { 21 | .transition( background-color 10ms linear 10ms ); // delay the tap highlight, it may just be a scroll 22 | background-color: @item-bg-tap; 23 | } 24 | 25 | // transition in and out of tap bg highlight 26 | &.Tappable-inactive { 27 | .transition( background-color 200ms ); 28 | } 29 | } 30 | 31 | // group headings 32 | // sits above the group, giving it a title 33 | 34 | .GroupHeader { 35 | color: @group-header-color; 36 | font-size: 80%; 37 | line-height: 1; 38 | text-transform: uppercase; 39 | 40 | // use margin for vertical spacing to take advantage of collapsing 41 | margin-bottom: @gutter-base; 42 | margin-top: @gutter-lg; 43 | padding-left: @padding-base-horizontal; 44 | padding-right: @padding-base-horizontal; 45 | } 46 | 47 | // group footer 48 | // sits below the group, use if the group needs further explanation 49 | 50 | .GroupFooter { 51 | color: @group-footer-color; 52 | font-size: 80%; 53 | line-height: 1.1; 54 | 55 | margin-bottom: @gutter-lg; 56 | margin-top: -(@gutter-lg / 2); 57 | padding-left: @padding-base-horizontal; 58 | padding-right: @padding-base-horizontal; 59 | } 60 | 61 | // body 62 | 63 | .GroupBody { 64 | line-height: 1.3; 65 | padding: @padding-base-vertical @padding-base-horizontal; 66 | 67 | & + & { 68 | box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.1); 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /less/components/List.less: -------------------------------------------------------------------------------- 1 | // 2 | // List 3 | // ============================== 4 | 5 | 6 | 7 | 8 | // Item 9 | // ------------------------------ 10 | 11 | .Item { 12 | .display-flex(); 13 | background: none; 14 | border: none; 15 | color: @item-color; 16 | line-height: 1; 17 | outline: none; 18 | padding: 0 0 0 @padding-base-horizontal; // create the ios-style gap on the left 19 | position: relative; 20 | text-align: left; 21 | width: 100%; 22 | z-index: 1; 23 | -webkit-appearance: none; 24 | 25 | // stop changes on various states when developing on desktop 26 | &:hover, 27 | &:active, 28 | &:focus { 29 | color: @item-color; 30 | text-decoration: none; 31 | } 32 | 33 | // add active state 34 | 35 | .Tappable-active > & { 36 | .transition( background-color 10ms linear 40ms ); // delay the tap highlight, it may just be a scroll 37 | background-color: @item-bg-tap; 38 | } 39 | 40 | // transition in and out of tap bg highlight 41 | 42 | .Tappable-inactive > & { 43 | .transition( background-color 200ms ); 44 | } 45 | } 46 | 47 | // provide the little arrow indicating a view will be shown on tap 48 | 49 | .Item--has-disclosure-arrow::after { 50 | .pseudo-ionicon(#ccc, @ionicon-var-chevron-right, @icon-size-base); 51 | .align-items(center); 52 | .display-flex(); 53 | height: 100%; 54 | position: absolute; 55 | right: @padding-base-horizontal; 56 | top: 0; 57 | } 58 | 59 | // align the interior 60 | 61 | .Item__inner { 62 | .display-flex(); 63 | .align-items(center); 64 | .justify-content(space-between); 65 | .retina-1px-border-bottom(@item-divider-color); 66 | // border-bottom: 1px solid @item-divider-color; 67 | min-height: @item-height; 68 | padding: @field-padding-vertical @field-padding-horizontal @field-padding-vertical 0; 69 | width: 100%; 70 | 71 | // alternative alignment (applied to the "list-item") 72 | .align-children-top > & { 73 | .align-items(flex-start); 74 | } 75 | .align-children-bottom > & { 76 | .align-items(flex-end); 77 | } 78 | 79 | // hide the last item's border 80 | .Item:last-child & { 81 | border-bottom: none; 82 | } 83 | 84 | // force borders when desired 85 | .Item.has-border-bottom & { 86 | border-bottom: 1px solid @item-divider-color; 87 | } 88 | .Item.has-border-top & { 89 | border-top: 1px solid @item-divider-color; 90 | } 91 | 92 | // add gap between media and content 93 | .Item__media + & { 94 | margin-left: @field-padding-horizontal; 95 | } 96 | 97 | // enforce border when explicitly defined 98 | .Item.has-border-bottom & { 99 | .retina-1px-border-bottom(@item-divider-color); 100 | } 101 | .Item.has-border-top & { 102 | .retina-1px-border-top(@item-divider-color); 103 | } 104 | } 105 | 106 | // wrap item content so text cropping works 107 | 108 | .Item__content { 109 | .flex(1, 0, 0); 110 | } 111 | 112 | // text 113 | 114 | .Item__title { 115 | .text-overflow(); 116 | 117 | // make room for descenders to stop the being cropped 118 | margin-bottom: -1px; 119 | padding-bottom: 1px; 120 | } 121 | .Item__subtitle { 122 | .text-overflow(); 123 | color: @gray; 124 | font-size: 85%; 125 | font-weight: 300; 126 | 127 | // make room for descenders to stop them being cropped 128 | margin-bottom: -1px; 129 | padding-bottom: 1px; 130 | } 131 | 132 | 133 | 134 | 135 | // Item Notes 136 | // ------------------------------ 137 | 138 | 139 | // base 140 | 141 | .Item__note { 142 | .display-flex(); 143 | position: relative; 144 | white-space: nowrap; 145 | 146 | 147 | // create a larger (but hidden) tappable area 148 | &:before { 149 | bottom: -@field-padding-vertical; 150 | content: " "; 151 | left: -@field-padding-horizontal; 152 | position: absolute; 153 | right: -@field-padding-horizontal; 154 | top: -@field-padding-vertical; 155 | } 156 | } 157 | 158 | 159 | // variants 160 | 161 | .Item__note--default { 162 | color: @gray-light; 163 | 164 | .Item__note__icon { 165 | color: #ccc; 166 | } 167 | } 168 | .Item__note--danger { color: @app-danger; } 169 | .Item__note--info { color: @app-info; } 170 | .Item__note--primary { color: @app-primary; } 171 | .Item__note--success { color: @app-success; } 172 | .Item__note--warning { color: @app-warning; } 173 | 174 | // one line 175 | 176 | .Item__note__label { white-space: nowrap; } 177 | 178 | 179 | // provide some space for the icon and get the size right 180 | 181 | .Item__note__icon { 182 | margin-left: (@padding-base-horizontal / 2); 183 | } 184 | 185 | 186 | 187 | 188 | // Item Media 189 | // ------------------------------ 190 | 191 | 192 | // base 193 | 194 | .Item__media { 195 | .display-flex(); 196 | .flex-wrap(nowrap); 197 | .align-items(center); 198 | } 199 | .Item__media--avatar { .square(@item-avatar-size); } 200 | .Item__media--icon { width: @item-icon-size; } 201 | .Item__media--thumbnail { .square(@item-thumbnail-size); } 202 | 203 | 204 | // icons 205 | 206 | .Item__media__icon { 207 | .square(@item-icon-size); 208 | border-radius: @border-radius-base; 209 | display: inline-block; 210 | line-height: @item-icon-size; 211 | text-align: center; 212 | vertical-align: middle; 213 | 214 | // actual icon 215 | 216 | &:before { 217 | .display-flex(); 218 | .align-items(center); 219 | .justify-content(center); 220 | font-size: @icon-size-large; 221 | height: 100%; 222 | } 223 | 224 | // align left 225 | &.left { 226 | left: @padding-base-horizontal; 227 | 228 | &.flush { 229 | left: 0; 230 | } 231 | } 232 | // align right 233 | &.right { 234 | right: @padding-base-horizontal; 235 | 236 | &.flush { 237 | right: 0; 238 | } 239 | } 240 | 241 | // add a box around the icon 242 | &.rounded { 243 | border-radius: @border-radius-base; 244 | background-color: @body-bg; 245 | } 246 | } 247 | 248 | // icon variants 249 | 250 | .Item__media__icon.primary-inverted { 251 | background-color: @app-primary; 252 | color: white; 253 | } 254 | .Item__media__icon.primary { 255 | color: @app-primary; 256 | } 257 | .Item__media__icon.default { 258 | background-color: @body-bg; 259 | color: @text-color; 260 | } 261 | 262 | // loading 263 | 264 | .Item__media__icon.ion-load-a:before, 265 | .Item__media__icon.ion-load-b:before, 266 | .Item__media__icon.ion-load-c:before, 267 | .Item__media__icon.ion-load-d:before { 268 | .animation( spin 1s linear infinite ); 269 | } 270 | 271 | // Avatars 272 | // ------------------------------ 273 | 274 | .Item__media__avatar { 275 | .square(@item-avatar-size); 276 | .translateY(-50%); 277 | background-color: mix(white, black, 70%); 278 | border-radius: 50%; 279 | color: white; 280 | display: inline-block; 281 | font-size: @icon-size-large; 282 | line-height: @item-avatar-size; 283 | left: @padding-base-horizontal; 284 | position: absolute; 285 | text-align: center; 286 | top: 50%; 287 | vertical-align: middle; 288 | 289 | > img { 290 | .square(100%); 291 | border-radius: 50%; 292 | display: block; 293 | height: auto; 294 | } 295 | } 296 | 297 | // Thumbnails 298 | // ------------------------------ 299 | 300 | .Item__media__thumbnail { 301 | .square(@item-thumbnail-size); 302 | margin-top: @padding-base-vertical; 303 | 304 | > img { 305 | border-radius: @border-radius-base; 306 | display: block; 307 | height: auto; 308 | max-width: 100%; 309 | } 310 | } 311 | 312 | // Headers 313 | // ------------------------------ 314 | 315 | .ListHeader { 316 | .retina-1px-border-top-and-bottom( @item-divider-color ); 317 | background-color: @body-bg; 318 | color: @list-header-color; 319 | display: block; 320 | font-size: @font-size-xs; 321 | font-weight: @font-weight-bold; 322 | line-height: 1.1; 323 | margin: 0; 324 | padding: (@padding-base-vertical / 2) @padding-base-horizontal; 325 | position: relative; // fallback 326 | text-transform: uppercase; 327 | z-index: 2; 328 | 329 | // sticky option 330 | &.sticky { 331 | position: -webkit-sticky; 332 | top: 0; 333 | } 334 | } 335 | 336 | -------------------------------------------------------------------------------- /less/components/NavigationBar.less: -------------------------------------------------------------------------------- 1 | // 2 | // Navigation Bar 3 | // ============================== 4 | 5 | 6 | 7 | 8 | // Base 9 | // --------------------------- 10 | 11 | .NavigationBar { 12 | .box-sizing(content-box); // allows us to set a fixed height whilst growing when padding is applied 13 | .retina-1px-border-bottom( rgba(0, 0, 0, 0.28) ); 14 | background-color: @headerbar-bg; 15 | line-height: @headerbar-height; 16 | height: @headerbar-height; 17 | position: relative; 18 | z-index: @view-transition-stack-top + 1; 19 | 20 | // make room for the iOS statusbar 21 | .device--iPhone & { 22 | padding-top: @statusbar-height; 23 | } 24 | } 25 | 26 | 27 | 28 | 29 | // Buttons - Common 30 | // --------------------------- 31 | 32 | 33 | /* 34 | * 1. force hardware accelaration - prepare for animation 35 | * removes antialising artefacts left by transform/animation 36 | * 2. remove browser default styles from