├── .gitignore
├── README.md
├── dist
├── bubb.min.css
├── bubb.min.css.gz
├── bubb.min.js
└── bubb.min.js.gz
├── docs
├── CNAME
├── assets
│ ├── bubb.min.css
│ ├── bubb.min.js
│ ├── demo.min.css
│ ├── demo.min.js
│ └── images
│ │ ├── bubb.gif
│ │ ├── bubb.png
│ │ ├── bubb_720.gif
│ │ ├── bubble_bobble.png
│ │ ├── circle.png
│ │ ├── circle.svg
│ │ ├── cirque.svg
│ │ ├── ghost.png
│ │ └── icons
│ │ ├── color
│ │ ├── facebook.svg
│ │ ├── reddit.svg
│ │ └── twitter.svg
│ │ ├── facebook.svg
│ │ ├── fill
│ │ ├── facebook.svg
│ │ ├── reddit.svg
│ │ └── twitter.svg
│ │ ├── github.svg
│ │ ├── reddit.svg
│ │ └── twitter.svg
├── index.html
└── libs
│ ├── css
│ └── tomorrow.min.css
│ └── js
│ └── highlight.min.js
├── gruntfile.js
├── html
└── index.html
├── js
├── demo.js
└── script.js
├── package-lock.json
├── package.json
└── scss
├── bubb.scss
└── demo.scss
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | node_modules
3 | .DS_Store
4 | _dev_*.*
5 | fonts
6 | .log
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | **Non-dependent**, non-fancy javascript _infotip_. **No CSS needed**.
4 |
5 | [](https://david-dm.org/frdnrdb/bubb)
6 | [](https://badge.fury.io/js/bubb)
7 | [](http://bubb.surge.sh)
8 |
9 | ---
10 |
11 | * **2.0.1** Options: toggle, autoDirection, autoHide
12 | * **next** Mobile solution
13 |
14 | ---
15 |
16 | #### Usage
17 | [→ See some examples](http://bubb.surge.sh)
18 |
19 | ```html
20 |
21 |
22 |
23 | info
24 | menu
25 |
26 |
44 | ```
45 | ```js
46 |
47 | // yarn add bubb | npm i --save bubb
48 | const bubb = require('bubb');
49 | ```
50 |
51 | #### Options setup
52 |
53 | ```js
54 |
55 | const config = {
56 | bubble: {
57 | text: 'content',
58 | _: {
59 | // ... bubble options
60 | }
61 | },
62 | bobble: {
63 | menu_item_1: 'content',
64 | menu_item_2: 'content',
65 | _: {
66 | // ... bobble options
67 | }
68 | },
69 | _: {
70 | // ... global options
71 | }
72 | }
73 |
74 | ```
75 |
76 | #### options
77 |
78 | ```js
79 |
80 | callback: false
81 | // function(){} overrides initial (or global) callback
82 | // boolean true adds click listener and reports to default callback
83 |
84 | transitionOff: false
85 | // boolean
86 |
87 | interactive: false
88 | // boolean, default true for menus and option callback
89 |
90 | hoverCallback: false
91 | // boolean, trigger callback on element:hover
92 |
93 | delay: false
94 | // int value, microseconds reveal delay
95 |
96 | autoHide: false
97 | // false or milliseconds
98 |
99 | toggle: false
100 | // boolean, activate tooltip with function call bubb.toggle(key)
101 |
102 | direction: false
103 | // string 'north', 'west' or 'east' (default false = 'south')
104 |
105 | autoDirection: false
106 | // boolean, screen edge proximity aware direction change
107 |
108 | anchor: false
109 | // string 'left' or 'right' (default false = 'centered')
110 |
111 | width: false
112 | // int value <= 100 (document width percentage)
113 | // css string with units (eg. '300px')
114 | // querySelector string (eg. 'section:first-of-type')
115 |
116 | borderRadius: '4px'
117 | // css string with units
118 |
119 | fontSize: '17px'
120 | // css string with units
121 |
122 | background: '#444'
123 | // css color string
124 |
125 | color: '#fff'
126 | // css color string
127 |
128 | class: false
129 | // string, className to target current bubb specifically
130 |
131 |
132 | ```
133 |
134 | #### Methods
135 |
136 | ```js
137 |
138 | bubb.refresh();
139 | // initialize new data-bubb elements added to DOM
140 |
141 | bubb.update(reference, content | options);
142 |
143 | bubb.update(menu_reference, options);
144 | bubb.update(menu_reference.menu_item, content);
145 |
146 | bubb.add(menu_reference.menu_item, content);
147 | bubb.remove(menu_reference.menu_item);
148 | // these methods adds or removes DOM elements
149 |
150 | ```
151 |
152 |
153 | #### Style overrides
154 | The content is targeted through **bubb-content** > **div**.
155 | The trigger element gets className **.bubb** *(and .bubb-menu)*.
156 | The bubb(le) tagname is **bubb-bobb**.
157 |
158 | ---
159 |
160 | #### Browser Support et cetera
161 |
162 | Missed that train. Feel free to contribute if you're on board.
163 |
--------------------------------------------------------------------------------
/dist/bubb.min.css:
--------------------------------------------------------------------------------
1 | [bubb] {
2 | position: relative; }
3 | [bubb]:before, [bubb]:after {
4 | position: absolute;
5 | pointer-events: none;
6 | visibility: hidden;
7 | z-index: 1;
8 | bottom: 0;
9 | opacity: 0; }
10 | [bubb]:before {
11 | content: '';
12 | width: 0;
13 | height: 0;
14 | border-bottom: 10px solid #444;
15 | border-right: 10px solid transparent;
16 | border-left: 10px solid transparent; }
17 | [bubb]:after {
18 | content: attr(bubb);
19 | padding: .75em .9em .85em;
20 | line-height: 1.1;
21 | font-size: 17px;
22 | text-align: center;
23 | border-radius: 5px;
24 | background-color: #444;
25 | color: #fff;
26 | min-width: 9em;
27 | text-rendering: optimizeLegibility;
28 | -webkit-font-smoothing: antialiased;
29 | -moz-osx-font-smoothing: grayscale;
30 | -webkit-touch-callout: none;
31 | -webkit-user-select: none;
32 | -khtml-user-select: none;
33 | -moz-user-select: none;
34 | -ms-user-select: none;
35 | user-select: none; }
36 | [bubb]:hover:before, [bubb]:hover:after {
37 | transition: transform 0.35s cubic-bezier(0, 0, 0, 1), opacity 0.35s cubic-bezier(0, 0, 0, 1);
38 | visibility: visible;
39 | opacity: 1; }
40 | [bubb][still]:hover:before, [bubb][still]:hover:after {
41 | transition-duration: 0s; }
42 | [bubb][delay]:hover:before, [bubb][delay]:hover:after {
43 | transition-delay: 0.75s; }
44 | [bubb][round]:after {
45 | border-radius: 30px !important; }
46 | [bubb][round][north][left]:after, [bubb][round][east][right]:after {
47 | border-radius: 30px 30px 30px 0 !important; }
48 | [bubb][round][north][right]:after, [bubb][round][west][right]:after {
49 | border-radius: 30px 30px 0 30px !important; }
50 | [bubb][round][left]:not([east]):not([west]):not([north]):after, [bubb][round][east][left]:after {
51 | border-radius: 0 30px 30px 30px !important; }
52 | [bubb][round][right]:not([east]):not([west]):not([north]):after, [bubb][round][west][left]:after {
53 | border-radius: 30px 0 30px 30px !important; }
54 | [bubb][large]:after {
55 | font-size: 20px !important; }
56 | [bubb]:not([east]):not([west]):not([north]):before, [bubb]:not([east]):not([west]):not([north]):after {
57 | left: 50%; }
58 | [bubb]:not([east]):not([west]):not([north]):before {
59 | transform: translate(-50%, calc( 10px + .065rem + 4px + 20px)); }
60 | [bubb]:not([east]):not([west]):not([north]):after {
61 | transform: translate(-50%, calc( 100% + 10px + 4px + 20px)); }
62 | [bubb]:not([east]):not([west]):not([north]):hover:before {
63 | transform: translate(-50%, calc( 10px + .065rem + 4px)); }
64 | [bubb]:not([east]):not([west]):not([north]):hover:after {
65 | transform: translate(-50%, calc( 100% + 10px + 4px)); }
66 | [bubb]:not([east]):not([west]):not([north])[left]:before, [bubb]:not([east]):not([west]):not([north])[right]:before {
67 | transform: translate(0, calc( 10px + .065rem + 4px + 20px)); }
68 | [bubb]:not([east]):not([west]):not([north])[left]:after, [bubb]:not([east]):not([west]):not([north])[right]:after {
69 | transform: translate(0, calc( 100% + 10px + 4px + 20px)); }
70 | [bubb]:not([east]):not([west]):not([north])[left]:hover:before, [bubb]:not([east]):not([west]):not([north])[right]:hover:before {
71 | transform: translate(0, calc( 10px + .065rem + 4px)); }
72 | [bubb]:not([east]):not([west]):not([north])[left]:hover:after, [bubb]:not([east]):not([west]):not([north])[right]:hover:after {
73 | transform: translate(0, calc( 100% + 10px + 4px)); }
74 | [bubb]:not([east]):not([west]):not([north])[left]:before, [bubb]:not([east]):not([west]):not([north])[left]:after {
75 | left: 0; }
76 | [bubb]:not([east]):not([west]):not([north])[left]:before {
77 | border-left: none; }
78 | [bubb]:not([east]):not([west]):not([north])[left]:after {
79 | border-radius: 0 5px 5px 5px; }
80 | [bubb]:not([east]):not([west]):not([north])[right]:before, [bubb]:not([east]):not([west]):not([north])[right]:after {
81 | left: auto;
82 | right: 0; }
83 | [bubb]:not([east]):not([west]):not([north])[right]:before {
84 | border-right: none; }
85 | [bubb]:not([east]):not([west]):not([north])[right]:after {
86 | border-radius: 5px 0 5px 5px; }
87 | [bubb][north]:before, [bubb][north]:after {
88 | left: 50%;
89 | top: 0;
90 | bottom: auto; }
91 | [bubb][north]:before {
92 | border-bottom: none;
93 | border-top: 10px solid #444;
94 | border-left-color: transparent;
95 | border-right-color: transparent;
96 | transform: translate(-50%, calc( -10px - .065rem - 4px - 20px)); }
97 | [bubb][north]:after {
98 | transform: translate(-50%, calc( -100% - 10px - 4px - 20px)); }
99 | [bubb][north]:hover:before {
100 | transform: translate(-50%, calc( -10px - .065rem - 4px)); }
101 | [bubb][north]:hover:after {
102 | transform: translate(-50%, calc( -100% - 10px - 4px)); }
103 | [bubb][north][left]:before, [bubb][north][right]:before {
104 | transform: translate(0, calc( -10px - .065rem - 4px - 20px)); }
105 | [bubb][north][left]:after, [bubb][north][right]:after {
106 | transform: translate(0, calc( -100% - 10px - 4px - 20px)); }
107 | [bubb][north][left]:hover:before, [bubb][north][right]:hover:before {
108 | transform: translate(0, calc( -10px - .065rem - 4px)); }
109 | [bubb][north][left]:hover:after, [bubb][north][right]:hover:after {
110 | transform: translate(0, calc( -100% - 10px - 4px)); }
111 | [bubb][north][left]:before, [bubb][north][left]:after {
112 | left: 0;
113 | top: 0;
114 | bottom: auto; }
115 | [bubb][north][left]:before {
116 | border-left: none; }
117 | [bubb][north][left]:after {
118 | border-radius: 5px 5px 5px 0; }
119 | [bubb][north][right]:before, [bubb][north][right]:after {
120 | left: auto;
121 | right: 0;
122 | top: 0;
123 | bottom: auto; }
124 | [bubb][north][right]:before {
125 | border-right: none; }
126 | [bubb][north][right]:after {
127 | border-radius: 5px 5px 0 5px; }
128 | [bubb][east]:before, [bubb][east]:after {
129 | left: auto;
130 | right: 0;
131 | top: 50%;
132 | bottom: auto; }
133 | [bubb][east]:before {
134 | border-left: none;
135 | border-right-color: #444; }
136 | [bubb][east]:before {
137 | border-top: 10px solid transparent;
138 | border-bottom-color: transparent;
139 | transform: translate(calc( 10px + .065rem + 4px + 20px), -50%); }
140 | [bubb][east]:after {
141 | transform: translate(calc( 100% + 10px + 4px + 20px), -50%); }
142 | [bubb][east]:hover:before {
143 | transform: translate(calc( 10px + .065rem + 4px), -50%); }
144 | [bubb][east]:hover:after {
145 | transform: translate(calc( 100% + 10px + 4px), -50%); }
146 | [bubb][east][left]:before {
147 | border-top: none;
148 | border-bottom-color: transparent;
149 | transform: translate(calc( 10px + .065rem + 4px + 20px), 0); }
150 | [bubb][east][left]:after {
151 | border-radius: 0 5px 5px 5px;
152 | transform: translate(calc( 100% + 10px + 4px + 20px), 0); }
153 | [bubb][east][left]:hover:before {
154 | transform: translate(calc( 10px + .065rem + 4px), 0); }
155 | [bubb][east][left]:hover:after {
156 | transform: translate(calc( 100% + 10px + 4px), 0); }
157 | [bubb][east][right]:before {
158 | border-top: 10px solid transparent;
159 | border-bottom: none;
160 | transform: translate(calc( 10px + .065rem + 4px + 20px), -100%); }
161 | [bubb][east][right]:after {
162 | border-radius: 5px 5px 5px 0;
163 | transform: translate(calc( 100% + 10px + 4px + 20px), -100%); }
164 | [bubb][east][right]:hover:before {
165 | transform: translate(calc( 10px + .065rem + 4px), -100%); }
166 | [bubb][east][right]:hover:after {
167 | transform: translate(calc( 100% + 10px + 4px), -100%); }
168 | [bubb][west]:before, [bubb][west]:after {
169 | left: 0;
170 | right: auto;
171 | top: 50%;
172 | bottom: auto; }
173 | [bubb][west]:before {
174 | border-right: none;
175 | border-top: 10px solid transparent;
176 | border-bottom-color: transparent;
177 | border-left-color: #444; }
178 | [bubb][west]:before {
179 | transform: translate(calc( -10px - .065rem - 4px - 20px), -50%); }
180 | [bubb][west]:after {
181 | transform: translate(calc( -100% - 10px - 4px - 20px), -50%); }
182 | [bubb][west]:hover:before {
183 | transform: translate(calc( -10px - .065rem - 4px), -50%); }
184 | [bubb][west]:hover:after {
185 | transform: translate(calc( -100% - 10px - 4px), -50%); }
186 | [bubb][west][left]:before {
187 | border-top: none;
188 | transform: translate(calc( -10px - .065rem - 4px - 20px), 0); }
189 | [bubb][west][left]:after {
190 | border-radius: 5px 0 5px 5px;
191 | transform: translate(calc( -100% - 10px - 4px - 20px), 0); }
192 | [bubb][west][left]:hover:before {
193 | transform: translate(calc( -10px - .065rem - 4px), 0); }
194 | [bubb][west][left]:hover:after {
195 | transform: translate(calc( -100% - 10px - 4px), 0); }
196 | [bubb][west][right]:before {
197 | border-bottom: none;
198 | transform: translate(calc( -10px - .065rem - 4px - 20px), -100%); }
199 | [bubb][west][right]:after {
200 | border-radius: 5px 5px 0 5px;
201 | transform: translate(calc( -100% - 10px - 4px - 20px), -100%); }
202 | [bubb][west][right]:hover:before {
203 | transform: translate(calc( -10px - .065rem - 4px), -100%); }
204 | [bubb][west][right]:hover:after {
205 | transform: translate(calc( -100% - 10px - 4px), -100%); }
206 |
--------------------------------------------------------------------------------
/dist/bubb.min.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frdnrdb/bubb/ea009ac3037d000b9f5a6c4fd0d78d18b5708cec/dist/bubb.min.css.gz
--------------------------------------------------------------------------------
/dist/bubb.min.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | TODO!
4 |
5 | * IMPROVE add eventEmitter
6 | - https://www.sitepoint.com/nodejs-events-and-eventemitter/
7 | - https://github.com/chrisdavies/eev
8 |
9 | * IMPROVE Implement some try/catch error handling and/or input type checks to prevent user input errors
10 |
11 | * ADD Implement dim-rest-of-page-option
12 |
13 | * TRY alternative tooltip layout (material-ui-dropdown-menu-ish) as option
14 | * TRY alternative theme (light (box-shadowed) or thin border) as option
15 |
16 | * CONSIDER Menu-style as part of package
17 |
18 | */
19 |
20 | (function(window, document) {var this$0 = this;
21 |
22 | var bubb = function(config, callback) {
23 |
24 | bubb.config = bubb.config || (typeof config === 'object' ? config : {});
25 | bubb.config._ = bubb.config._ || config._ || {};
26 | bubb.callback = bubb.callback || (typeof bubb.config._.callback === 'function' && bubb.config._.callback) || (typeof callback === 'function' && callback);
27 |
28 | // class bubb indicates the element has already been initiated/ configured
29 | var bubbs = arguments[0] === 'update' ? [arguments[1]] : Array.from( document.querySelectorAll('[data-bubb]:not(.bubb)') );
30 |
31 | if (!bubbs.length) return;
32 |
33 | !bubb._initialized && initBubb();
34 |
35 | bubbs.forEach(buildElement);
36 |
37 | }
38 |
39 | var buildElement = function(_trigger ) {
40 |
41 | var key = _trigger.dataset.bubb.trim(),
42 | data = bubb.config[key] || key,
43 |
44 | chck = typeof data === 'object',
45 | opts = chck && data._, // config contains options _
46 | only = opts && Object.keys(data).length === 1, // options only, use key as content
47 | menu = chck && !data.hasOwnProperty('text'); // config indicates a menu
48 |
49 | only && (data.text = key);
50 |
51 | var props = !menu ? opts ? ['text'] : [false] : Object.keys(data),
52 | bindMenu = typeof bubb.callback === 'function' || typeof bubb.config._.callback === 'function',
53 | triggerPosition = window.getComputedStyle(_trigger).position;
54 |
55 | _trigger._bubb = {
56 | key: key,
57 | config: setElementConfiguration(opts),
58 | type: menu ? 'menu' : opts ? 'opts' : 'string',
59 | markup: props.reduce( buildElementMarkup.bind(this$0, key), '' ),
60 | bind: (menu && bindMenu) || (opts && (typeof opts.callback === 'function' || typeof opts.callback === 'boolean'))
61 | };
62 |
63 | triggerPosition && !triggerPosition.match(/absolute|fixed|relative/) && (_trigger.style.position = 'relative');
64 |
65 | _trigger.classList.add('bubb');
66 |
67 | bubb.triggers[key] = _trigger;
68 |
69 | bindElement(_trigger);
70 |
71 | };
72 |
73 | var setElementConfiguration = function(opts ) {
74 |
75 | return availableOptions.reduce( function(config, option) {
76 | opts && opts.hasOwnProperty(option) ? config[option] = opts[option]
77 | : bubb.config._.hasOwnProperty(option) ? config[option] = bubb.config._[option]
78 | : false;
79 | return config;
80 | }, {});
81 |
82 | };
83 |
84 | var buildElementMarkup = function(key, markup, prop) {
85 |
86 | if (prop === '_') return markup;
87 |
88 | var content = prop ? bubb.config[key][prop] : bubb.config[key] || key,
89 | selector = key + (prop && prop !== 'text' ? '.' + prop : ''),
90 | attribute = ((" data-bubb-value=\"" + selector) + "\"");
91 |
92 | return markup += (("
" + content) + "
");
93 |
94 | };
95 |
96 | var bindElement = function(_trigger) {
97 |
98 | var bubbEvents = _trigger._bubb.config.toggle ? [] : ['mouseenter', 'mouseleave'];
99 |
100 | if (_trigger._bubb.bind) bubbEvents.push('mousedown');
101 |
102 | bubbEvents.forEach( function(event ) {return _trigger.addEventListener(event, eventHandler, false)} );
103 |
104 | };
105 |
106 | var configureElement = function(_trigger ) {
107 |
108 | var trigger = _trigger._bubb,
109 | bubble = bubb._element,
110 | config = bubble._config = trigger.config;
111 |
112 | bubble.style.visibility = 'hidden';
113 |
114 | bubble._elementContent.innerHTML = trigger.markup;
115 |
116 | bubble._bind = (trigger.bind && (config.interactive !== false)) || config.toggle;
117 |
118 | bubble.className = config.class || '';
119 |
120 | setWidth(config, _trigger, bubble);
121 |
122 | trigger.type === 'menu' && _trigger.classList.add('bubb-menu');
123 |
124 | _trigger.appendChild(bubble);
125 | bubb._trigger = _trigger;
126 | bubb._visible = false;
127 |
128 | appendStyles(bubb._element, '_bubblePreactive');
129 |
130 | };
131 |
132 | var bubbShow = function() {
133 |
134 | appendStyles(bubb._element, '_bubbleActive');
135 | bubb._visible = true;
136 |
137 | };
138 |
139 | var bubbHide = function(e) {
140 |
141 | appendStyles(bubb._element, ['_bubbleInactive', '_bubblePreactive']);
142 | bubb._visible = false;
143 |
144 | };
145 |
146 | var autoDirection = function(e, target) {
147 |
148 | if (!e) return;
149 |
150 | var h = bubb._element.offsetHeight || 150;
151 | var w = bubb._element.offsetWidth || 150;
152 | var d = target._bubb.config.direction;
153 |
154 | var rect = target.getBoundingClientRect();
155 |
156 | var limits = {
157 | w: rect.left < w,
158 | e: rect.right > (window.innerWidth - w),
159 | n: rect.top < h,
160 | s: rect.bottom > (window.innerHeight - h)
161 | }
162 |
163 | if (d && (d === 'east' || d === 'west')) {
164 | bubb._autoDirection = limits.w ? 'east' : limits.e ? 'west' : false;
165 | bubb._autoAnchor = limits.n ? 'left' : limits.s ? 'right' : false;
166 | }
167 | else {
168 | bubb._autoDirection = limits.s ? 'north' : limits.n ? 'south' : false;
169 | bubb._autoAnchor = false;
170 | }
171 |
172 | return bubb._autoDirection;
173 |
174 | };
175 |
176 | var eventHandler = function(e) {
177 |
178 | if (!this._bubb) return;
179 |
180 | // ---> configure (if autoDirection in config OR trigger new element)
181 |
182 | (
183 | ((bubb.config._.autoDirection || this._bubb.config.autoDirection)
184 | && autoDirection(e, this)
185 | )
186 | || (bubb._trigger !== this)
187 | )
188 | && configureElement(this);
189 |
190 | // ---> reveal or hide
191 |
192 | window.clearTimeout(bubb._timerOn);
193 | window.clearTimeout(bubb._timerOff);
194 |
195 | e.type === 'mouseenter'
196 | ? bubb._timerOn = window.setTimeout( bubbShow, (this._bubb.config.delay | 0) || 0)
197 | : e.type !== 'mousedown' && bubbHide(e);
198 |
199 | if (this._bubb.config.autoHide) {
200 | bubb._timerOff = window.setTimeout( bubbHide, this._bubb.config.autoHide + (this._bubb.config.delay | 0) );
201 | }
202 |
203 | // ---> leave
204 |
205 | if (!this._bubb.bind || e.type === 'mouseleave') return;
206 |
207 | // ---> callback
208 |
209 | var hover = this._bubb.config.hoverCallback,
210 | bubbvalue = hover ? this.dataset.bubb : e.target.dataset.bubbValue || e.target.parentNode.dataset.bubbValue || e.target.parentNode.parentNode.dataset.bubbValue;
211 |
212 | if (!bubbvalue) return;
213 |
214 | var thiscallback = (typeof this._bubb.config.callback === 'function' && this._bubb.config.callback) || bubb.callback,
215 | item = this.querySelector((("[data-bubb-value=\"" + (this.dataset.bubb)) + "\"]")) || e.target;
216 |
217 | thiscallback(bubbvalue, item, this, e.type);
218 |
219 | }
220 |
221 | var isTrigger = function(node, check) {
222 |
223 | if (!node) return;
224 | if (node._bubb && (check ? node === bubb._trigger : true)) return node;
225 | return isTrigger(node.parentNode);
226 |
227 | };
228 |
229 | var clickOutside = function(e) {
230 |
231 | // _toggler initiated the toggle
232 |
233 | !bubb._toggler && (bubb._toggler = e.target);
234 |
235 | // avoid toggle initiator to negate the toggle
236 |
237 | if (bubb._toggler === e.target) return;
238 |
239 | // click inside; the caller is the toggled element
240 |
241 | if (isTrigger(e.target, bubb._trigger)) return;
242 |
243 | // release _toggler, remove event listener and hide bubb
244 |
245 | bubb._toggler = false;
246 | window.removeEventListener('click', clickOutside, false);
247 | eventHandler.call(bubb._trigger, {target: bubb._trigger, type: 'mouseleave'});
248 |
249 | }
250 |
251 | var toggle = function() {
252 |
253 | var element = arguments[0],
254 | _trigger = (typeof element === 'object' && element) || bubb.triggers[element];
255 |
256 | if (!_trigger || !_trigger._bubb.config.toggle) {
257 | console.error('bubb: trying to toggle a non-existing or non-toggled element');
258 | return;
259 | }
260 |
261 | //_trigger !== bubb._trigger && configureElement(_trigger);
262 |
263 | window[ (bubb._visible ? 'remove' : 'add') + 'EventListener']('click', clickOutside, false);
264 | eventHandler.call(_trigger, {target: _trigger, type: bubb._visible ? 'mouseleave' : 'mouseenter'});
265 |
266 | };
267 |
268 | var update = function() {
269 |
270 | var key = arguments[0], contentOrConfig = arguments[1];
271 |
272 | if (typeof key !== 'string' || !contentOrConfig) return;
273 |
274 | var updateOptions = typeof contentOrConfig === 'object',
275 | menu = key.split('.').reduce( function(obj, val, i) {
276 | obj[['key','val'][i]] = val;
277 | return obj;
278 | }, {}),
279 | _trigger = document.querySelector((("[data-bubb=\"" + (menu.key || key)) + "\"]"));
280 |
281 | if (!_trigger && !updateOptions) return;
282 |
283 | if (!_trigger) {
284 | console.error('bubb: trying to update a non-existing element');
285 | return;
286 | }
287 |
288 | // update added DOM element - when bubb.refresh() has not been used
289 |
290 | if (!_trigger._bubb) bubb('update', _trigger);
291 |
292 | bubb._trigger = false;
293 |
294 | // update element
295 |
296 | if (!updateOptions) {
297 | _trigger._bubb.markup = _trigger._bubb.markup.replace(new RegExp(((".*?
")), (("" + contentOrConfig) + "
"));
298 | return;
299 | }
300 |
301 | // update element config
302 |
303 | var bindDefault = typeof contentOrConfig.callback === 'boolean' && !_trigger._bubb.bind,
304 | bindSelf = typeof contentOrConfig.callback === 'function' && !_trigger._bubb.config.hasOwnProperty('callback'),
305 | bindHover = contentOrConfig.hoverCallback && !_trigger._bubb.config.hoverCallback;
306 |
307 | if (bindDefault || bindSelf || bindHover) _trigger._bubb.bind = true;
308 |
309 | if (bindDefault || bindSelf) _trigger.addEventListener( 'mousedown', eventHandler, false);
310 |
311 | Object.keys(contentOrConfig).forEach( function(updatedKey ) {
312 | if (!~availableOptions.indexOf(updatedKey)) return;
313 | if (_trigger) _trigger._bubb.config[updatedKey] = contentOrConfig[updatedKey];
314 | else bubb.config['_'][updatedKey] = contentOrConfig[updatedKey];
315 | });
316 |
317 | // update main config
318 |
319 | updateMainConfig(key, contentOrConfig, updateOptions, _trigger);
320 |
321 | };
322 |
323 | var addOrRemove = function() {
324 |
325 | if (arguments.length === 0) {
326 | bubb();
327 | return;
328 | }
329 |
330 | var key = arguments[0], value = arguments[1],
331 | menu = key.split('.').reduce( function(obj, val, i) {
332 | obj[['key','val'][i]] = val;
333 | return obj;
334 | }, {});
335 |
336 | if (!menu.val || !bubb.config[menu.key]) return;
337 |
338 | // add menu item
339 |
340 | bubb._trigger = false;
341 |
342 | if (value && typeof value === 'string' && !bubb.config[menu.key][menu.val]) {
343 |
344 | bubb.config[menu.key][menu.val] = value;
345 |
346 | document.querySelector((("[data-bubb=\"" + (menu.key)) + "\"]"))._bubb.markup += (("" + value) + "
");
347 | return;
348 |
349 | }
350 |
351 | // remove menu item
352 |
353 | if (!value && bubb.config[menu.key][menu.val]) {
354 |
355 | delete bubb.config[menu.key][menu.val];
356 |
357 | var _trigger = document.querySelector((("[data-bubb=\"" + (menu.key)) + "\"]"));
358 | _trigger._bubb.markup = _trigger._bubb.markup.replace(new RegExp(((".*?
")), '');
359 |
360 | }
361 |
362 | };
363 |
364 | var setWidth = function(config, _trigger, _bubble) {
365 |
366 | var input = config.width,
367 | anchor = config.anchor,
368 | direction = config.direction,
369 | sideways = direction === 'east' || direction === 'west';
370 |
371 | // '300px', '3em'
372 | if (typeof input === 'string' && parseInt(input)) {
373 | _bubble.style.width = input;
374 | return;
375 | }
376 |
377 | // 33, '33' || 'section > div'
378 | var width = (input | 0) || document.querySelector(input);
379 |
380 | if (!width) {
381 | _bubble.style.width = '100%';
382 | return;
383 | }
384 |
385 | var padding = 30,
386 | fill = typeof width === 'object',
387 | bodyw = document.body.offsetWidth,
388 | box = _trigger.getBoundingClientRect(),
389 | boxm = box.width/2,
390 | boxl = box.left,
391 | boxr = box.right,
392 | inputWidth = fill ? width.offsetWidth : (width === 100 ? bodyw - padding*2 : (width * bodyw) / 100);
393 |
394 | if (anchor || sideways) {
395 |
396 | var newWidth$0 = !sideways && anchor
397 | ? anchor === 'left'
398 | ? bodyw - boxl - padding : anchor === 'right'
399 | ? bodyw - ( bodyw - boxr ) - padding
400 | : false
401 | : direction === 'east'
402 | ? bodyw - boxr - padding : direction === 'west'
403 | ? boxl - padding
404 | : false;
405 |
406 | if (newWidth$0) {
407 | _bubble.style.width = Math.min(inputWidth, newWidth$0) + 'px';
408 | return;
409 | }
410 |
411 | }
412 |
413 | var newWidth = Math.min(inputWidth, ((boxl + boxm > bodyw/2 ? bodyw - boxr + boxm: boxl + boxm) - padding) * 2);
414 |
415 | _bubble.style.width = newWidth + 'px';
416 | _bubble.style.left = (boxm - newWidth/2) + 'px';
417 | _bubble._elementTip.style.left = newWidth/2 + 'px';
418 |
419 | };
420 |
421 | var updateMainConfig = function(key, contentOrConfig, updateOptions, _trigger) {
422 |
423 | var typeMenu = _trigger._bubb.type === 'menu',
424 | typeOptions = _trigger._bubb.type === 'opts',
425 | keyVal = ~key.indexOf('.') && key.split('.'),
426 | menu = keyVal ? keyVal.reduce( function(obj, val, i) {
427 | obj[['key','val'][i]] = val;
428 | return obj;
429 | }, {}) : {};
430 |
431 | if (!typeMenu && !typeOptions && !updateOptions) {
432 | bubb.config[key] = contentOrConfig;
433 | return;
434 | }
435 |
436 | var prop = menu.key || key;
437 |
438 | if (!updateOptions) {
439 | bubb.config[prop][menu.val || 'text'] = contentOrConfig;
440 | return;
441 | }
442 |
443 | if (!typeMenu && !typeOptions) {
444 | bubb.config[prop] = {
445 | text: bubb.config[prop],
446 | _: contentOrConfig
447 | };
448 | _trigger._bubb.type === 'opts';
449 | return;
450 | }
451 |
452 | bubb.config[prop]['_'] = bubb.config[prop]['_'] || contentOrConfig;
453 | Object.assign(bubb.config[prop]['_'], contentOrConfig);
454 |
455 | };
456 |
457 | var styleVariables = {
458 | tipsize: '12px',
459 | offset: '.15em',
460 | distance: '20px',
461 | easing: 'cubic-bezier(0,0,0,1)',
462 | duration: '.3s',
463 | background: '#444',
464 | color: '#fff',
465 | rounding: '4px',
466 | fontsize: '17px'
467 | };
468 |
469 | var styles = {
470 | _bubble: {
471 | position: 'absolute',
472 | zIndex: '99',
473 | display: 'block',
474 | padding: '.75em .9em .85em',
475 | lineHeight: '1.1',
476 | textAlign: 'center',
477 | cursor: 'default',
478 | minWidth: '150px',
479 | width: '100%',
480 | boxSizing: 'border-box',
481 | textRendering: 'optimizeLegibility',
482 | WebkitFontSmoothing: 'antialiased',
483 | MozOsxFontSmoothing: 'grayscale',
484 | wordWrap: 'break-word',
485 | hyphens: 'auto',
486 | WebkitTouchCallout: 'none',
487 | WebkitUserSelect: 'none',
488 | KhtmlUserSelect: 'none',
489 | MozUserSelect: 'none',
490 | MsUserSelect: 'none',
491 | userSelect: 'none'
492 | },
493 | _bubbleInactive: {
494 | visibility: 'hidden',
495 | pointerEvents: 'none',
496 | opacity: '0',
497 | },
498 | _bubblePreactive: {
499 | background: styleVariables['background'],
500 | borderBottomColor: styleVariables['background'],
501 | color: styleVariables['color'],
502 | fontSize: styleVariables['fontsize'],
503 | },
504 | _bubbleActive: {
505 | transitionProperty: 'opacity, transform',
506 | transitionDuration: styleVariables['duration'],
507 | transitionTimingFunction: styleVariables['easing'],
508 | pointerEvents: 'all',
509 | opacity: '1',
510 | visibility: 'visible'
511 | },
512 | _bubbleInteractive: {
513 | position: 'absolute',
514 | zIndex: '-1',
515 | display: 'none',
516 | width: (("calc( 100% + ( " + (styleVariables['tipsize'])) + (" + " + (styleVariables['offset'])) + " ) * 2 )"),
517 | height: (("calc( 100% + ( " + (styleVariables['tipsize'])) + (" + " + (styleVariables['offset'])) + " ) * 2 )"),
518 | top: (("calc( -1 * ( " + (styleVariables['tipsize'])) + (" + " + (styleVariables['offset'])) + " ) )"),
519 | left: (("calc( -1 * ( " + (styleVariables['tipsize'])) + (" + " + (styleVariables['offset'])) + " ) )"),
520 | pointerEvents: 'all',
521 | background: 'transparent'
522 | },
523 | _bubbleTip: {
524 | position: 'absolute',
525 | width: '0',
526 | height: '0',
527 | borderLeft: (("" + (styleVariables['tipsize'])) + " solid transparent"),
528 | borderRight: (("" + (styleVariables['tipsize'])) + " solid transparent"),
529 | borderBottomWidth: styleVariables['tipsize'],
530 | borderBottomStyle: 'solid',
531 | borderBottomColor: 'inherit'
532 | }
533 | };
534 |
535 | var styleTransforms = {
536 | positive: {
537 | active: (("calc( 100% + " + (styleVariables['tipsize'])) + (" + " + (styleVariables['offset'])) + " )"),
538 | inactive: (("calc( 100% + " + (styleVariables['tipsize'])) + (" + " + (styleVariables['offset'])) + (" + " + (styleVariables['distance'])) + " )")
539 | },
540 | negative: {
541 | active: (("calc( -100% - " + (styleVariables['tipsize'])) + (" - " + (styleVariables['offset'])) + " )"),
542 | inactive: (("calc( -100% - " + (styleVariables['tipsize'])) + (" - " + (styleVariables['offset'])) + (" - " + (styleVariables['distance'])) + " )")
543 | }
544 | };
545 |
546 | var styleDirections = {
547 | x: {
548 | east: styleTransforms.positive,
549 | west: styleTransforms.negative
550 | },
551 | y: {
552 | south: styleTransforms.positive,
553 | north: styleTransforms.negative,
554 | }
555 | };
556 |
557 | var styleRoundings = {
558 | south: {
559 | left: [0,1,1,1],
560 | right: [1,0,1,1]
561 | },
562 | north: {
563 | left: [1,1,1,0],
564 | right: [1,1,0,1]
565 | },
566 | east: {
567 | left: [0,1,1,1],
568 | right: [1,1,1,0]
569 | },
570 | west: {
571 | left: [1,0,1,1],
572 | right: [1,1,0,1]
573 | }
574 | };
575 |
576 | var evalAnchor = function(left, anchor) {return anchor ? ( (anchor === 'left' && left) || (anchor === 'right' && !left) ? 0 : 'auto' ) : left ? '50%' : 'auto'};
577 |
578 | var stylePositions = {
579 | south: {
580 | left: evalAnchor.bind(this, true),
581 | right: evalAnchor.bind(this, false),
582 | top: function(anchor, tip) {return tip ? (("calc( 2px - " + (styleVariables['tipsize'])) + " )") : 'auto'},
583 | bottom: function(anchor, tip) {return tip ? 'auto' : 0}
584 | },
585 | north: {
586 | left: function(anchor, tip) {return evalAnchor(true, tip && anchor ? anchor === 'left' ? 'left' : 'right' : anchor)},
587 | right: function(anchor, tip) {return evalAnchor(false, tip && anchor ? anchor === 'left' ? 'left' : 'right' : anchor)},
588 | top: function(anchor, tip) {return tip ? 'auto' : 0},
589 | bottom: function(anchor, tip) {return tip ? (("calc( 2px - " + (styleVariables['tipsize'])) + " )") : 'auto'}
590 | },
591 | east: {
592 | left: function(anchor, tip) {return tip ? (("calc( 2px - " + (styleVariables['tipsize'])) + " )") : 'auto'},
593 | right: function(anchor, tip) {return tip ? 'auto' : 0},
594 | top: function(anchor, tip) {return tip ? evalAnchor(true, anchor) : !anchor || (anchor === 'left') ? '50%' : 'auto'},
595 | bottom: function(anchor, tip) {return tip ? evalAnchor(false, anchor) : anchor === 'right' ? '50%' : 'auto'}
596 | },
597 | west: {
598 | left: function(anchor, tip) {return tip ? 'auto' : 0},
599 | right: function(anchor, tip) {return tip ? (("calc( 2px - " + (styleVariables['tipsize'])) + " )") : 'auto'},
600 | top: function(anchor, tip) {return tip ? evalAnchor(true, anchor) : !anchor || (anchor === 'left') ? '50%' : 'auto'},
601 | bottom: function(anchor, tip) {return tip ? evalAnchor(false, anchor) : anchor === 'right' ? '50%' : 'auto'}
602 | }
603 | };
604 |
605 | var tipTransforms = {
606 | south: {
607 | center: 'translate(-50%, 0)',
608 | left: 'translate(-25%, 50%) rotate(90deg)',
609 | right: 'translate(25%, 50%) rotate(-90deg)'
610 | },
611 | north: {
612 | center: 'translate(-50%, 0) rotate(180deg)',
613 | left: 'translate(-25%, -50%) rotate(90deg)',
614 | right: 'translate(25%, -50%) rotate(-90deg)'
615 | },
616 | east: {
617 | center: 'translate(-25%, -50%) rotate(-90deg)',
618 | left: 'translate(0, 0) rotate(180deg)',
619 | right: 'translate(0, 0)'
620 | },
621 | west: {
622 | center: 'translate(25%, -50%) rotate(90deg)',
623 | left: 'translate(0, 0) rotate(180deg)',
624 | right: 'translate(0, 0)'
625 | }
626 | };
627 |
628 | var setDirectionSpecificStyles = function(element, config, activeOrInactive) {
629 |
630 | var direction = bubb._autoDirection || config.direction || 'south',
631 | anchor = bubb._autoAnchor || config.anchor;
632 |
633 | var setBubbleTransform = function(xy ) {return ( typeof styleDirections[xy][direction] === 'object' && styleDirections[xy][direction][activeOrInactive] ) || ( anchor ? '0' : '-50%' )};
634 |
635 | element.style.transform = (("translate(" + (setBubbleTransform('x'))) + (", " + (setBubbleTransform('y'))) + ")");
636 |
637 | if (activeOrInactive === 'active') return;
638 |
639 | element._elementInteractive.style.display = element._bind ? 'block' : 'none';
640 |
641 | element._elementTip.style.transform = tipTransforms[direction][anchor || 'center'];
642 |
643 | ['left', 'right', 'top', 'bottom'].forEach( function(position ) {
644 | element.style[position] = stylePositions[direction][position](anchor);
645 | element._elementTip.style[position] = stylePositions[direction][position](anchor, true);
646 | });
647 |
648 | element.style.borderRadius = (styleRoundings[direction][anchor] || [1]).reduce( function(str, chk) { return str += (chk ? config.borderRadius || styleVariables['rounding'] : 0) + ' '; }, '');
649 |
650 | };
651 |
652 | var appendStyles = function(element, keys, init) {
653 |
654 | var config = element._config || {};
655 |
656 | keys = typeof keys === 'string' ? [keys] : keys;
657 |
658 | keys.forEach( function(key ) {
659 |
660 | var active = key === '_bubbleActive',
661 | preactive = key === '_bubblePreactive',
662 |
663 | still = active && config.transitionOff,
664 | background = preactive && config.background,
665 | color = preactive && config.color,
666 | fontsize = preactive && config.fontSize;
667 |
668 | Object.keys(styles[key]).forEach( function(style ) {
669 | element.style[style] = init ? styles[key][style]
670 | : style === 'transitionDuration' && still ? '0s'
671 | : (style === 'background' || style === 'borderBottomColor') && background ? background
672 | : style === 'color' && color ? color
673 | : style === 'fontSize' && fontsize ? fontsize
674 | : styles[key][style];
675 | });
676 |
677 | if (!init && (active || preactive)) setDirectionSpecificStyles(element, config, active ? 'active' : 'inactive');
678 |
679 | });
680 |
681 | };
682 |
683 | var setMethodProxies = function() {
684 |
685 | bubb.update = function() {return update.apply(this$0, arguments)};
686 | bubb.add = bubb.refresh = bubb.remove = function() {return addOrRemove.apply(this$0, arguments)};
687 | bubb.toggle = function() {return toggle.apply(this$0, arguments)};
688 |
689 | };
690 |
691 | var createBubbElements = function() {
692 |
693 | bubb._element = document.createElement('bubb-bobb');
694 |
695 | var element = bubb._element,
696 | tagMap = {
697 | _elementInteractive: 'bubb-interactive',
698 | _elementTip: 'bubb-tip',
699 | _elementContent: 'bubb-content'
700 | };
701 |
702 | for (var tag in tagMap) {
703 | element[tag] = document.createElement(tagMap[tag]);
704 | element.appendChild(element[tag]);
705 | }
706 |
707 | appendStyles(element, ['_bubble', '_bubbleInactive'], true);
708 | appendStyles(element._elementTip, '_bubbleTip', true);
709 | appendStyles(element._elementInteractive, '_bubbleInteractive', true);
710 |
711 | bubb._den = document.createElement('bubb-den');
712 | bubb._den.style.display = 'none';
713 | bubb._den.appendChild(element);
714 |
715 | document.body.appendChild(bubb._den);
716 |
717 | };
718 |
719 | var listenToBubbEvents = function() {
720 |
721 | var hideOrKeep = function() {return bubb._element._bind || appendStyles(bubb._element, '_bubbleInactive', true)};
722 | bubb._element.addEventListener('mouseenter', hideOrKeep, false);
723 |
724 | };
725 |
726 | var initBubb = function() {
727 |
728 | bubb._initialized = true;
729 | bubb.triggers = {};
730 |
731 | setMethodProxies();
732 | createBubbElements();
733 | listenToBubbEvents();
734 |
735 | };
736 |
737 | var availableOptions = [
738 | 'callback',
739 | 'hoverCallback',
740 | 'background',
741 | 'color',
742 | 'transitionOff',
743 | 'interactive',
744 | 'delay',
745 | 'width',
746 | 'fontSize',
747 | 'class',
748 | 'anchor',
749 | 'direction',
750 | 'borderRadius',
751 | 'autoHide',
752 | 'toggle',
753 | 'autoDirection'
754 | ];
755 |
756 | // const isMobile = (typeof window.orientation !== "undefined") || ~window.navigator.userAgent.indexOf('IEMobile') ? true : false;
757 |
758 | typeof module !== 'undefined' && module.exports ? module.exports = bubb : window.bubb = bubb;
759 |
760 | })(window, document);
761 |
--------------------------------------------------------------------------------
/dist/bubb.min.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frdnrdb/bubb/ea009ac3037d000b9f5a6c4fd0d78d18b5708cec/dist/bubb.min.js.gz
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | bubb.surge.sh
2 |
--------------------------------------------------------------------------------
/docs/assets/bubb.min.css:
--------------------------------------------------------------------------------
1 | [bubb] {
2 | position: relative; }
3 | [bubb]:before, [bubb]:after {
4 | position: absolute;
5 | pointer-events: none;
6 | visibility: hidden;
7 | z-index: 1;
8 | bottom: 0;
9 | opacity: 0; }
10 | [bubb]:before {
11 | content: '';
12 | width: 0;
13 | height: 0;
14 | border-bottom: 10px solid #444;
15 | border-right: 10px solid transparent;
16 | border-left: 10px solid transparent; }
17 | [bubb]:after {
18 | content: attr(bubb);
19 | padding: .75em .9em .85em;
20 | line-height: 1.1;
21 | font-size: 17px;
22 | text-align: center;
23 | border-radius: 5px;
24 | background-color: #444;
25 | color: #fff;
26 | min-width: 9em;
27 | text-rendering: optimizeLegibility;
28 | -webkit-font-smoothing: antialiased;
29 | -moz-osx-font-smoothing: grayscale;
30 | -webkit-touch-callout: none;
31 | -webkit-user-select: none;
32 | -khtml-user-select: none;
33 | -moz-user-select: none;
34 | -ms-user-select: none;
35 | user-select: none; }
36 | [bubb]:hover:before, [bubb]:hover:after {
37 | transition: transform 0.35s cubic-bezier(0, 0, 0, 1), opacity 0.35s cubic-bezier(0, 0, 0, 1);
38 | visibility: visible;
39 | opacity: 1; }
40 | [bubb][still]:hover:before, [bubb][still]:hover:after {
41 | transition-duration: 0s; }
42 | [bubb][delay]:hover:before, [bubb][delay]:hover:after {
43 | transition-delay: 0.75s; }
44 | [bubb][round]:after {
45 | border-radius: 30px !important; }
46 | [bubb][round][north][left]:after, [bubb][round][east][right]:after {
47 | border-radius: 30px 30px 30px 0 !important; }
48 | [bubb][round][north][right]:after, [bubb][round][west][right]:after {
49 | border-radius: 30px 30px 0 30px !important; }
50 | [bubb][round][left]:not([east]):not([west]):not([north]):after, [bubb][round][east][left]:after {
51 | border-radius: 0 30px 30px 30px !important; }
52 | [bubb][round][right]:not([east]):not([west]):not([north]):after, [bubb][round][west][left]:after {
53 | border-radius: 30px 0 30px 30px !important; }
54 | [bubb][large]:after {
55 | font-size: 20px !important; }
56 | [bubb]:not([east]):not([west]):not([north]):before, [bubb]:not([east]):not([west]):not([north]):after {
57 | left: 50%; }
58 | [bubb]:not([east]):not([west]):not([north]):before {
59 | transform: translate(-50%, calc( 10px + .065rem + 4px + 20px)); }
60 | [bubb]:not([east]):not([west]):not([north]):after {
61 | transform: translate(-50%, calc( 100% + 10px + 4px + 20px)); }
62 | [bubb]:not([east]):not([west]):not([north]):hover:before {
63 | transform: translate(-50%, calc( 10px + .065rem + 4px)); }
64 | [bubb]:not([east]):not([west]):not([north]):hover:after {
65 | transform: translate(-50%, calc( 100% + 10px + 4px)); }
66 | [bubb]:not([east]):not([west]):not([north])[left]:before, [bubb]:not([east]):not([west]):not([north])[right]:before {
67 | transform: translate(0, calc( 10px + .065rem + 4px + 20px)); }
68 | [bubb]:not([east]):not([west]):not([north])[left]:after, [bubb]:not([east]):not([west]):not([north])[right]:after {
69 | transform: translate(0, calc( 100% + 10px + 4px + 20px)); }
70 | [bubb]:not([east]):not([west]):not([north])[left]:hover:before, [bubb]:not([east]):not([west]):not([north])[right]:hover:before {
71 | transform: translate(0, calc( 10px + .065rem + 4px)); }
72 | [bubb]:not([east]):not([west]):not([north])[left]:hover:after, [bubb]:not([east]):not([west]):not([north])[right]:hover:after {
73 | transform: translate(0, calc( 100% + 10px + 4px)); }
74 | [bubb]:not([east]):not([west]):not([north])[left]:before, [bubb]:not([east]):not([west]):not([north])[left]:after {
75 | left: 0; }
76 | [bubb]:not([east]):not([west]):not([north])[left]:before {
77 | border-left: none; }
78 | [bubb]:not([east]):not([west]):not([north])[left]:after {
79 | border-radius: 0 5px 5px 5px; }
80 | [bubb]:not([east]):not([west]):not([north])[right]:before, [bubb]:not([east]):not([west]):not([north])[right]:after {
81 | left: auto;
82 | right: 0; }
83 | [bubb]:not([east]):not([west]):not([north])[right]:before {
84 | border-right: none; }
85 | [bubb]:not([east]):not([west]):not([north])[right]:after {
86 | border-radius: 5px 0 5px 5px; }
87 | [bubb][north]:before, [bubb][north]:after {
88 | left: 50%;
89 | top: 0;
90 | bottom: auto; }
91 | [bubb][north]:before {
92 | border-bottom: none;
93 | border-top: 10px solid #444;
94 | border-left-color: transparent;
95 | border-right-color: transparent;
96 | transform: translate(-50%, calc( -10px - .065rem - 4px - 20px)); }
97 | [bubb][north]:after {
98 | transform: translate(-50%, calc( -100% - 10px - 4px - 20px)); }
99 | [bubb][north]:hover:before {
100 | transform: translate(-50%, calc( -10px - .065rem - 4px)); }
101 | [bubb][north]:hover:after {
102 | transform: translate(-50%, calc( -100% - 10px - 4px)); }
103 | [bubb][north][left]:before, [bubb][north][right]:before {
104 | transform: translate(0, calc( -10px - .065rem - 4px - 20px)); }
105 | [bubb][north][left]:after, [bubb][north][right]:after {
106 | transform: translate(0, calc( -100% - 10px - 4px - 20px)); }
107 | [bubb][north][left]:hover:before, [bubb][north][right]:hover:before {
108 | transform: translate(0, calc( -10px - .065rem - 4px)); }
109 | [bubb][north][left]:hover:after, [bubb][north][right]:hover:after {
110 | transform: translate(0, calc( -100% - 10px - 4px)); }
111 | [bubb][north][left]:before, [bubb][north][left]:after {
112 | left: 0;
113 | top: 0;
114 | bottom: auto; }
115 | [bubb][north][left]:before {
116 | border-left: none; }
117 | [bubb][north][left]:after {
118 | border-radius: 5px 5px 5px 0; }
119 | [bubb][north][right]:before, [bubb][north][right]:after {
120 | left: auto;
121 | right: 0;
122 | top: 0;
123 | bottom: auto; }
124 | [bubb][north][right]:before {
125 | border-right: none; }
126 | [bubb][north][right]:after {
127 | border-radius: 5px 5px 0 5px; }
128 | [bubb][east]:before, [bubb][east]:after {
129 | left: auto;
130 | right: 0;
131 | top: 50%;
132 | bottom: auto; }
133 | [bubb][east]:before {
134 | border-left: none;
135 | border-right-color: #444; }
136 | [bubb][east]:before {
137 | border-top: 10px solid transparent;
138 | border-bottom-color: transparent;
139 | transform: translate(calc( 10px + .065rem + 4px + 20px), -50%); }
140 | [bubb][east]:after {
141 | transform: translate(calc( 100% + 10px + 4px + 20px), -50%); }
142 | [bubb][east]:hover:before {
143 | transform: translate(calc( 10px + .065rem + 4px), -50%); }
144 | [bubb][east]:hover:after {
145 | transform: translate(calc( 100% + 10px + 4px), -50%); }
146 | [bubb][east][left]:before {
147 | border-top: none;
148 | border-bottom-color: transparent;
149 | transform: translate(calc( 10px + .065rem + 4px + 20px), 0); }
150 | [bubb][east][left]:after {
151 | border-radius: 0 5px 5px 5px;
152 | transform: translate(calc( 100% + 10px + 4px + 20px), 0); }
153 | [bubb][east][left]:hover:before {
154 | transform: translate(calc( 10px + .065rem + 4px), 0); }
155 | [bubb][east][left]:hover:after {
156 | transform: translate(calc( 100% + 10px + 4px), 0); }
157 | [bubb][east][right]:before {
158 | border-top: 10px solid transparent;
159 | border-bottom: none;
160 | transform: translate(calc( 10px + .065rem + 4px + 20px), -100%); }
161 | [bubb][east][right]:after {
162 | border-radius: 5px 5px 5px 0;
163 | transform: translate(calc( 100% + 10px + 4px + 20px), -100%); }
164 | [bubb][east][right]:hover:before {
165 | transform: translate(calc( 10px + .065rem + 4px), -100%); }
166 | [bubb][east][right]:hover:after {
167 | transform: translate(calc( 100% + 10px + 4px), -100%); }
168 | [bubb][west]:before, [bubb][west]:after {
169 | left: 0;
170 | right: auto;
171 | top: 50%;
172 | bottom: auto; }
173 | [bubb][west]:before {
174 | border-right: none;
175 | border-top: 10px solid transparent;
176 | border-bottom-color: transparent;
177 | border-left-color: #444; }
178 | [bubb][west]:before {
179 | transform: translate(calc( -10px - .065rem - 4px - 20px), -50%); }
180 | [bubb][west]:after {
181 | transform: translate(calc( -100% - 10px - 4px - 20px), -50%); }
182 | [bubb][west]:hover:before {
183 | transform: translate(calc( -10px - .065rem - 4px), -50%); }
184 | [bubb][west]:hover:after {
185 | transform: translate(calc( -100% - 10px - 4px), -50%); }
186 | [bubb][west][left]:before {
187 | border-top: none;
188 | transform: translate(calc( -10px - .065rem - 4px - 20px), 0); }
189 | [bubb][west][left]:after {
190 | border-radius: 5px 0 5px 5px;
191 | transform: translate(calc( -100% - 10px - 4px - 20px), 0); }
192 | [bubb][west][left]:hover:before {
193 | transform: translate(calc( -10px - .065rem - 4px), 0); }
194 | [bubb][west][left]:hover:after {
195 | transform: translate(calc( -100% - 10px - 4px), 0); }
196 | [bubb][west][right]:before {
197 | border-bottom: none;
198 | transform: translate(calc( -10px - .065rem - 4px - 20px), -100%); }
199 | [bubb][west][right]:after {
200 | border-radius: 5px 5px 0 5px;
201 | transform: translate(calc( -100% - 10px - 4px - 20px), -100%); }
202 | [bubb][west][right]:hover:before {
203 | transform: translate(calc( -10px - .065rem - 4px), -100%); }
204 | [bubb][west][right]:hover:after {
205 | transform: translate(calc( -100% - 10px - 4px), -100%); }
206 |
--------------------------------------------------------------------------------
/docs/assets/demo.min.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | @font-face {
3 | font-family: 'Walsheim';
4 | font-weight: bold;
5 | font-style: bold;
6 | src: url("./fonts/gt-walsheim-bold-web.ttf"); }
7 |
8 | @font-face {
9 | font-family: 'Walsheim';
10 | font-weight: normal;
11 | font-style: normal;
12 | src: url("./fonts/gt-walsheim-light-web.ttf"); }
13 |
14 | * {
15 | text-rendering: optimizeLegibility;
16 | -webkit-font-smoothing: antialiased;
17 | -moz-osx-font-smoothing: grayscale; }
18 |
19 | html {
20 | overflow-y: scroll; }
21 |
22 | body {
23 | padding: 0;
24 | margin: 0;
25 | background: #fff;
26 | color: #444;
27 | font-family: 'Walsheim', arial, sans-serif;
28 | font-weight: normal;
29 | font-size: 1.5em;
30 | line-height: 1;
31 | letter-spacing: .025rem;
32 | position: relative;
33 | min-height: 100vh;
34 | box-sizing: border-box;
35 | max-width: 100vw;
36 | overflow: hidden; }
37 |
38 | header {
39 | font-size: 1em;
40 | padding: 2.5em calc( (100vw - 720px) / 2);
41 | box-sizing: border-box;
42 | width: 100%;
43 | position: relative;
44 | display: block;
45 | background: #444;
46 | color: #fff; }
47 | header:last-of-type {
48 | background: #FFDEDE;
49 | color: #444; }
50 | header:last-of-type a {
51 | color: #444; }
52 | header:last-of-type a:hover {
53 | color: #4797B1; }
54 | header aside {
55 | margin-top: .75em; }
56 | header aside a {
57 | color: #F7F3CE;
58 | text-decoration: none;
59 | font-size: 50%;
60 | text-transform: uppercase;
61 | letter-spacing: 2px;
62 | margin: .5em .5em 0 0; }
63 | header aside a:hover {
64 | opacity: .75; }
65 | header aside a:before {
66 | content: '';
67 | max-height: 0;
68 | overflow: hidden;
69 | display: block; }
70 | header aside a.inline:before {
71 | content: ' | ';
72 | max-height: none;
73 | display: inline-block;
74 | opacity: .35;
75 | margin-right: .75em;
76 | font-size: 150%;
77 | position: relative;
78 | top: 4px; }
79 | header aside a.first {
80 | display: block;
81 | padding-top: 1em;
82 | margin-left: 0; }
83 | header i {
84 | font-style: normal;
85 | font-size: 50%;
86 | opacity: .65;
87 | position: relative;
88 | margin-right: -.75em; }
89 | header i:first-of-type {
90 | top: -.85em; }
91 | header i:last-of-type {
92 | left: -1.1em; }
93 | header b {
94 | font-weight: bold; }
95 | header span {
96 | float: right; }
97 | header:after {
98 | content: none;
99 | position: absolute;
100 | width: 100vw;
101 | height: 7.3em;
102 | display: block;
103 | background: rgba(0, 0, 0, 0.035);
104 | top: -3.25em;
105 | left: -7.5vw; }
106 |
107 | code {
108 | font-size: 70%;
109 | overflow-x: scroll; }
110 | code::-webkit-scrollbar {
111 | width: 0px;
112 | background: transparent; }
113 |
114 | pre {
115 | font-size: 16px;
116 | opacity: .35;
117 | transition: opacity .25s linear;
118 | position: relative;
119 | z-index: 0;
120 | filter: grayscale(100%); }
121 | pre:hover {
122 | opacity: 1;
123 | filter: grayscale(0%); }
124 |
125 | .opaque pre {
126 | opacity: 1;
127 | filter: grayscale(0%);
128 | padding: 0;
129 | border: none;
130 | font-size: 18px; }
131 |
132 | section {
133 | position: relative;
134 | max-width: 720px;
135 | margin: 0 auto;
136 | padding: .5em 0 4em;
137 | box-sizing: border-box;
138 | display: block;
139 | width: 90%; }
140 | section:first-of-type {
141 | padding: 0 0 4em; }
142 | section:first-of-type .header {
143 | font-weight: bold;
144 | width: 100%;
145 | height: 6em;
146 | line-height: 6em;
147 | text-decoration: none;
148 | background: #F7F3CE;
149 | display: block;
150 | padding: 0 5%;
151 | box-sizing: border-box;
152 | text-transform: uppercase;
153 | letter-spacing: 2px;
154 | font-size: 11px;
155 | color: #444;
156 | margin-bottom: 4em;
157 | white-space: nowrap; }
158 | section > p {
159 | font-size: 70%;
160 | line-height: 1.3;
161 | color: #888; }
162 | section article {
163 | position: relative; }
164 | section article > p {
165 | font-size: 70%;
166 | line-height: 1.3;
167 | color: #888; }
168 | section article > span > div {
169 | display: inline-block;
170 | font-size: 95%;
171 | margin-top: 1em;
172 | padding-bottom: 2px;
173 | border-bottom: 2px dotted #fad;
174 | cursor: help;
175 | position: static; }
176 | section article > span > div:hover {
177 | border-color: transparent; }
178 | section article > span > i:after {
179 | content: '⟵ :hover';
180 | display: inline-block;
181 | white-space: nowrap;
182 | border-bottom: none;
183 | margin-left: 12px;
184 | font-size: 75%;
185 | opacity: .35; }
186 | section article > span > i.click:after {
187 | content: '⟵ :click'; }
188 | section article > div {
189 | display: inline-block;
190 | font-size: 95%;
191 | margin-top: 1em;
192 | padding-bottom: 2px;
193 | border-bottom: 2px dotted #fad;
194 | cursor: help;
195 | position: static; }
196 | section article > div:hover {
197 | border-color: transparent; }
198 | section article > i:after {
199 | content: '⟵ :hover';
200 | display: inline-block;
201 | white-space: nowrap;
202 | border-bottom: none;
203 | margin-left: 12px;
204 | font-size: 75%;
205 | opacity: .35; }
206 | section article > i.click:after {
207 | content: '⟵ :click'; }
208 |
209 | footer {
210 | background: #F7F3CE;
211 | display: block;
212 | position: relative;
213 | height: 4.5em;
214 | width: 100%;
215 | line-height: 4.5em; }
216 | footer a {
217 | position: absolute;
218 | top: 0;
219 | left: calc( (100vw - 720px) / 2);
220 | text-transform: uppercase;
221 | letter-spacing: 2px;
222 | font-size: 11px;
223 | text-decoration: none;
224 | font-weight: bold;
225 | color: #444;
226 | /*
227 | &:last-of-type {
228 | left: auto;
229 | right: calc( (100vw - 720px) / 2 );
230 | &:before {
231 | content: 'Download Bubb on';
232 | }
233 | }
234 | */ }
235 | footer a:before {
236 | display: inline-block;
237 | font-weight: normal;
238 | margin-right: 6px;
239 | content: 'By'; }
240 |
241 | #eventsDisplay {
242 | background: rgba(15, 241, 206, 0.7);
243 | padding: 0 7.5vw;
244 | box-sizing: border-box;
245 | line-height: 100vh;
246 | height: 100vh;
247 | width: 100vw;
248 | font-size: 65%;
249 | font-style: normal;
250 | text-transform: uppercase;
251 | text-align: center;
252 | letter-spacing: 2px;
253 | position: fixed;
254 | top: 0;
255 | right: 0;
256 | pointer-events: none;
257 | z-index: 9999999; }
258 | #eventsDisplay[color="0"] {
259 | background: rgba(255, 222, 222, 0.6); }
260 | #eventsDisplay[color="1"] {
261 | background: rgba(247, 243, 206, 0.6); }
262 | #eventsDisplay[color="2"] {
263 | background: rgba(197, 236, 190, 0.6); }
264 | #eventsDisplay[color="3"] {
265 | background: rgba(71, 151, 177, 0.6); }
266 | #eventsDisplay[color="4"] {
267 | background: rgba(84, 101, 107, 0.6); }
268 | #eventsDisplay:empty {
269 | visibility: hidden; }
270 |
271 | .wait {
272 | opacity: 0;
273 | transition: opacity .25s linear; }
274 | .done .wait {
275 | opacity: 1; }
276 |
277 | section ul {
278 | font-size: 70%;
279 | list-style: none;
280 | color: #888;
281 | padding-left: 50px;
282 | line-height: 1.2;
283 | padding-top: .75em; }
284 | section ul li {
285 | margin-bottom: inherit;
286 | position: relative; }
287 | section ul li:before {
288 | position: absolute;
289 | top: .6em;
290 | height: 1px;
291 | width: 35px;
292 | left: -50px;
293 | background: #888;
294 | display: block;
295 | content: ''; }
296 | section ul li u {
297 | text-decoration: none;
298 | display: block;
299 | font-weight: bold;
300 | padding-bottom: 1px; }
301 | section ul:last-of-type li u {
302 | display: inline-block;
303 | margin-right: .5em; }
304 |
305 | hr {
306 | margin: 2.5em 0 1em;
307 | opacity: .25; }
308 | section:last-of-type hr {
309 | margin: 2em 0 1em; }
310 | section:last-of-type hr:last-of-type {
311 | margin: 1.5em 0; }
312 |
313 | .bubble-bobble {
314 | width: 44px;
315 | height: 44px;
316 | background: url(/assets/images/bubble_bobble.png);
317 | background-repeat: no-repeat;
318 | background-size: 100%;
319 | position: fixed;
320 | bottom: 1em;
321 | right: 1em; }
322 | .bubble-bobble:hover {
323 | background-position: 0 100%; }
324 |
325 | ul.share-buttons {
326 | list-style: none;
327 | padding: 0;
328 | margin: .75em 0 0; }
329 | ul.share-buttons.device {
330 | background: #444;
331 | padding: 2em 0;
332 | text-align: center; }
333 |
334 | ul.share-buttons li {
335 | display: inline; }
336 | ul.share-buttons li:before {
337 | content: none; }
338 | ul.share-buttons li:hover img {
339 | opacity: .75; }
340 | ul.share-buttons li img {
341 | min-width: 32px;
342 | min-height: 32px;
343 | max-width: 32px;
344 | margin: 0 4px;
345 | display: inline-block; }
346 |
347 | ul.share-buttons .sr-only {
348 | position: absolute;
349 | clip: rect(1px 1px 1px 1px);
350 | clip: rect(1px, 1px, 1px, 1px);
351 | padding: 0;
352 | border: 0;
353 | height: 1px;
354 | width: 1px;
355 | overflow: hidden; }
356 |
357 | device {
358 | display: none; }
359 |
360 | #toggle {
361 | float: left;
362 | width: 50%; }
363 |
364 | #toggler {
365 | float: right;
366 | width: 50%;
367 | text-align: right;
368 | -webkit-touch-callout: none;
369 | -webkit-user-select: none;
370 | -khtml-user-select: none;
371 | -moz-user-select: none;
372 | -ms-user-select: none;
373 | user-select: none; }
374 | #toggler pre {
375 | visibility: hidden; }
376 |
377 | #added {
378 | display: block;
379 | width: 100%;
380 | clear: both; }
381 |
382 | #all-directions {
383 | position: absolute;
384 | top: 100px;
385 | right: 0;
386 | width: 80px;
387 | height: 78px;
388 | border: none;
389 | background: transparent;
390 | border-radius: 50%;
391 | background-image: url(images/cirque.svg);
392 | background-repeat: no-repeat; }
393 | #all-directions > div {
394 | display: inline-block;
395 | z-index: 1;
396 | position: absolute;
397 | width: auto;
398 | height: auto;
399 | cursor: crosshair; }
400 | #all-directions > div:after {
401 | content: '';
402 | background: rgba(255, 255, 255, 0.8);
403 | min-width: 24px;
404 | min-height: 30px;
405 | position: absolute;
406 | transition: background .25s ease; }
407 | #all-directions > div:hover:after {
408 | background: rgba(255, 255, 255, 0); }
409 | #all-directions > div.n:nth-child(1) {
410 | top: 7.5%;
411 | left: 25%; }
412 | #all-directions > div.n:nth-child(1):after {
413 | transform: translate(-50%, -50%) rotate(-30deg); }
414 | #all-directions > div.n:nth-child(2) {
415 | z-index: 9;
416 | top: 0;
417 | left: 50%; }
418 | #all-directions > div.n:nth-child(2):after {
419 | transform: translate(-50%, -50%); }
420 | #all-directions > div.n:nth-child(3) {
421 | top: 7.5%;
422 | left: 75%; }
423 | #all-directions > div.n:nth-child(3):after {
424 | transform: translate(-50%, -50%) rotate(30deg); }
425 | #all-directions > div.e:nth-child(4) {
426 | right: 7.5%;
427 | top: 25%; }
428 | #all-directions > div.e:nth-child(4):after {
429 | transform: translate(-50%, -50%) rotate(60deg); }
430 | #all-directions > div.e:nth-child(5) {
431 | right: 0;
432 | top: 50%; }
433 | #all-directions > div.e:nth-child(5):after {
434 | transform: translate(-50%, -50%) rotate(90deg); }
435 | #all-directions > div.e:nth-child(6) {
436 | right: 7.5%;
437 | top: 75%; }
438 | #all-directions > div.e:nth-child(6):after {
439 | transform: translate(-50%, -50%) rotate(120deg); }
440 | #all-directions > div.s:nth-child(7) {
441 | bottom: 7.5%;
442 | left: 25%; }
443 | #all-directions > div.s:nth-child(7):after {
444 | transform: translate(-50%, -50%) rotate(30deg); }
445 | #all-directions > div.s:nth-child(8) {
446 | z-index: 9;
447 | bottom: 0;
448 | left: 50%; }
449 | #all-directions > div.s:nth-child(8):after {
450 | transform: translate(-50%, -50%); }
451 | #all-directions > div.s:nth-child(9) {
452 | bottom: 7.5%;
453 | left: 75%; }
454 | #all-directions > div.s:nth-child(9):after {
455 | transform: translate(-50%, -50%) rotate(-30deg); }
456 | #all-directions > div.w:nth-child(10) {
457 | left: 7.5%;
458 | top: 25%; }
459 | #all-directions > div.w:nth-child(10):after {
460 | transform: translate(-50%, -50%) rotate(120deg); }
461 | #all-directions > div.w:nth-child(11) {
462 | left: 0;
463 | top: 50%; }
464 | #all-directions > div.w:nth-child(11):after {
465 | transform: translate(-50%, -50%) rotate(90deg); }
466 | #all-directions > div.w:nth-child(12) {
467 | left: 7.5%;
468 | top: 75%; }
469 | #all-directions > div.w:nth-child(12):after {
470 | transform: translate(-50%, -50%) rotate(60deg); }
471 | #all-directions > span {
472 | position: absolute;
473 | width: 0;
474 | height: 0;
475 | background: pink;
476 | left: 50%;
477 | top: 50%;
478 | transform: rotate(0deg);
479 | transition: all .25s ease;
480 | opacity: 1;
481 | /*
482 | > i {
483 | position: absolute;
484 | width:0;
485 | height: 0;
486 | border-left: 8px solid transparent;
487 | border-right: 8px solid transparent;
488 | border-bottom: 20px solid #444;
489 | border-top: none;
490 | left: 50%;
491 | top: 50%;
492 | transform: translate(-50%, -60%);
493 | }
494 | */ }
495 | #all-directions > span > i img {
496 | position: absolute;
497 | width: 35px;
498 | height: 35px;
499 | left: 50%;
500 | top: 50%;
501 | transform: translate(-50%, -50%); }
502 | #all-directions:hover > span {
503 | opacity: 1; }
504 |
505 | /*
506 | --- bubb modifiers
507 | */
508 | .bubb {
509 | -webkit-touch-callout: none;
510 | -webkit-user-select: none;
511 | -khtml-user-select: none;
512 | -moz-user-select: none;
513 | -ms-user-select: none;
514 | user-select: none; }
515 | .bubb p {
516 | padding: 0;
517 | margin: 0; }
518 | .bubb b {
519 | font-weight: bold;
520 | letter-spacing: 1px;
521 | font-size: 90%; }
522 |
523 | .bubb img {
524 | max-width: 100%;
525 | display: block;
526 | margin: 0 auto .5em; }
527 |
528 | .bubb-menu bubb-bobb {
529 | padding: 0 !important;
530 | background: #FFDEDE !important;
531 | border-bottom-color: #FFDEDE !important;
532 | color: #444 !important; }
533 | .bubb-menu bubb-bobb div {
534 | padding: .75em 0;
535 | text-transform: uppercase;
536 | letter-spacing: 2px;
537 | font-size: 12px;
538 | font-weight: bold;
539 | transition: background 0.3s cubic-bezier(0, 0, 0, 0.75);
540 | cursor: pointer; }
541 | .bubb-menu bubb-bobb div:first-child {
542 | padding-top: 1em; }
543 | .bubb-menu bubb-bobb div:hover {
544 | text-decoration: line-through; }
545 | .bubb-menu bubb-bobb div:nth-child(2) {
546 | background: #F7F3CE;
547 | color: #444; }
548 | .bubb-menu bubb-bobb div:nth-child(3) {
549 | background: #C5ECBE;
550 | color: #444; }
551 | .bubb-menu bubb-bobb div:nth-child(4) {
552 | background: #4797B1;
553 | color: #fff; }
554 | .bubb-menu bubb-bobb div:nth-child(5) {
555 | background: #54656b;
556 | border-radius: 0 0 4px 4px;
557 | color: #fff; }
558 |
559 | .tipcolor {
560 | border-bottom-color: #FFDEDE !important; }
561 |
562 | @media all and (max-width: 550px) {
563 | hide {
564 | display: none; }
565 | device {
566 | display: block; }
567 | body {
568 | font-size: 5vw;
569 | padding-bottom: 0; }
570 | header, footer {
571 | padding: 1.5em; }
572 | section {
573 | text-align: center;
574 | padding: 4em 1.5em; }
575 | section > span {
576 | display: block; }
577 | section i {
578 | display: none; }
579 | section:first-of-type .header {
580 | margin-bottom: 3em;
581 | position: relative;
582 | left: -5vw;
583 | width: 100vw;
584 | text-align: center;
585 | font-size: 50%; }
586 | section:first-of-type .header.download {
587 | background: #C5ECBE;
588 | margin-bottom: 0; }
589 | section:first-of-type .header.info {
590 | background: #FFDEDE;
591 | margin-bottom: 0; }
592 | section:first-of-type .header.image {
593 | height: auto;
594 | padding: 0;
595 | margin: 0;
596 | display: block; }
597 | section:last-of-type {
598 | padding: 1.35em 0; }
599 | section > p, section ul {
600 | font-size: 70%; }
601 | section ul {
602 | padding-left: 0; }
603 | section ul li:before {
604 | content: none; }
605 | footer {
606 | position: static;
607 | text-align: center;
608 | padding: 2em 0 4em;
609 | line-height: 1.2; }
610 | footer a {
611 | position: static; }
612 | header {
613 | font-size: 1.25em; }
614 | header aside a {
615 | line-height: 1.5;
616 | font-size: 35%; }
617 | header aside a.inline:before {
618 | content: ''; }
619 | header aside a.first {
620 | display: inline-block; }
621 | pre {
622 | display: none; }
623 | .opaque pre {
624 | display: block;
625 | text-align: left;
626 | opacity: 1;
627 | filter: none;
628 | font-size: 80%;
629 | padding: 1em 10% 0; }
630 | .bubble-bobble {
631 | width: 32px;
632 | height: 32px;
633 | background-size: 32px; }
634 | .bubble-bobble:hover {
635 | background-position: 0 -32px; }
636 | #all-directions {
637 | display: none; } }
638 |
--------------------------------------------------------------------------------
/docs/assets/images/bubb.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frdnrdb/bubb/ea009ac3037d000b9f5a6c4fd0d78d18b5708cec/docs/assets/images/bubb.gif
--------------------------------------------------------------------------------
/docs/assets/images/bubb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frdnrdb/bubb/ea009ac3037d000b9f5a6c4fd0d78d18b5708cec/docs/assets/images/bubb.png
--------------------------------------------------------------------------------
/docs/assets/images/bubb_720.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frdnrdb/bubb/ea009ac3037d000b9f5a6c4fd0d78d18b5708cec/docs/assets/images/bubb_720.gif
--------------------------------------------------------------------------------
/docs/assets/images/bubble_bobble.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frdnrdb/bubb/ea009ac3037d000b9f5a6c4fd0d78d18b5708cec/docs/assets/images/bubble_bobble.png
--------------------------------------------------------------------------------
/docs/assets/images/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frdnrdb/bubb/ea009ac3037d000b9f5a6c4fd0d78d18b5708cec/docs/assets/images/circle.png
--------------------------------------------------------------------------------
/docs/assets/images/circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
8 |
18 |
19 |
--------------------------------------------------------------------------------
/docs/assets/images/cirque.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
8 |
18 |
19 |
--------------------------------------------------------------------------------
/docs/assets/images/ghost.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frdnrdb/bubb/ea009ac3037d000b9f5a6c4fd0d78d18b5708cec/docs/assets/images/ghost.png
--------------------------------------------------------------------------------
/docs/assets/images/icons/color/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
9 |
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 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/color/reddit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
17 |
18 |
20 |
22 |
25 |
26 |
27 |
30 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/color/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/fill/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/fill/reddit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/fill/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/reddit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/docs/assets/images/icons/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
8 |
9 |
10 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bubb - Euphemism for a JS tooltip
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Bubb InfotipJS
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Minimal, non-dependent , non-fancy JS infotip. No CSS needed.
47 | Infotip? Seriously? Isn't that just a euphemism for tooltip? It sure is.
48 | Still, Bubb has a couple of pros to consider.
49 |
50 | Content by config Each instance's content is referenced from a single configuration object. Plain convenience, lucid maintenance.
51 | Context menus Bubb can act as a lazy man's UI
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Basic
60 |
61 |
64 |
65 | Callback on hover
66 |
67 |
68 | Toggle
69 |
70 |
71 | Toggle remote
72 |
73 |
74 |
75 | API and options
76 |
77 |
78 |
79 |
80 | Override styling
81 | The content is targeted through bubb-content > div .
82 | The trigger element gets className .bubb (and .bubb-menu)
83 | the bubble tagname is bubb-bobb
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | Bubb InfotipCSS
93 |
97 |
98 |
99 |
100 |
101 | Pure CSS infotip, <div bubb="attribute only " />.
102 |
103 | Default
104 | Direction
105 | Delay
106 | No transitions
107 | Delayed, rounded...
108 |
109 |
110 |
111 | bubb Initiate with attribute content. Default direction south
112 | nort, east, west Change direction
113 | left, right Anchor the tip
114 | still Remove the transition
115 | delay Delay the reveal
116 | round Add some border radius
117 | large Subtly enlarge
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
130 |
131 |
134 |
135 |
136 |
137 |
138 |
139 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/docs/libs/css/tomorrow.min.css:
--------------------------------------------------------------------------------
1 | .hljs-comment,.hljs-quote{color:#8e908c}.hljs-variable,.hljs-template-variable,.hljs-tag,.hljs-name,.hljs-selector-id,.hljs-selector-class,.hljs-regexp,.hljs-deletion{color:#c82829}.hljs-number,.hljs-built_in,.hljs-builtin-name,.hljs-literal,.hljs-type,.hljs-params,.hljs-meta,.hljs-link{color:#f5871f}.hljs-attribute{color:#eab700}.hljs-string,.hljs-symbol,.hljs-bullet,.hljs-addition{color:#718c00}.hljs-title,.hljs-section{color:#4271ae}.hljs-keyword,.hljs-selector-tag{color:#8959a8}.hljs{display:block;overflow-x:auto;background:white;color:#4d4d4c;padding:0.5em}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}
--------------------------------------------------------------------------------
/gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | require('load-grunt-tasks')(grunt);
4 |
5 | grunt.registerTask('build', ['sass','cssmin','es6transpiler','uglify','copy:build','compress','replace','clean']);
6 | grunt.registerTask('serve:build', ['build','connect','open','watch']);
7 | grunt.registerTask('serve', ['sass','cssmin','es6transpiler','copy:dev','connect','open','watch']);
8 | grunt.registerTask('publish', ['build','shell:git_add','shell:git_commit','shell:npm_version','shell:git_push','shell:npm_publish','shell:surge']);
9 |
10 | grunt.initConfig ({
11 | pkg: grunt.file.readJSON('package.json'),
12 | sass: {
13 | demo: {
14 | files: {
15 | 'scss/demo.css' : 'scss/demo.scss',
16 | 'scss/bubb.css' : 'scss/bubb.scss'
17 | }
18 | }
19 | },
20 | cssnano: {
21 | dist: {
22 | files: {
23 | 'docs/assets/demo.min.css': 'scss/demo.css',
24 | 'docs/assets/bubb.min.css': 'scss/bubb.css'
25 | }
26 | }
27 | },
28 | cssmin: {
29 | css: {
30 | files: [{
31 | expand: true,
32 | cwd: 'scss/',
33 | src: ['*.css'],
34 | dest: 'docs/assets',
35 | ext: '.min.css'
36 | }]
37 | }
38 | },
39 | es6transpiler: {
40 | main: {
41 | files: {
42 | 'js/script_transpiled.js': 'js/script.js',
43 | 'js/demo_transpiled.js': 'js/demo.js'
44 | }
45 | }
46 | },
47 | uglify: {
48 | options: {
49 | banner: '/* ' +
50 | '<%= pkg.name %> v<%= pkg.version %> ' +
51 | '(<%= grunt.template.today("yyyy-mm-dd") %>) | ' +
52 | '<%= pkg.homepage %> | ' +
53 | '(c) <%= grunt.template.today("yyyy") %> <%= pkg.author %> | ' +
54 | 'licensed <%= pkg.license %> ' +
55 | '*/\n'
56 | },
57 | main: {
58 | files: {
59 | 'docs/assets/demo.min.js': ['js/demo_transpiled.js'],
60 | 'docs/assets/bubb.min.js': ['js/script_transpiled.js']
61 | }
62 | }
63 | },
64 | copy: {
65 | build: {
66 | files: [
67 | {
68 | src: 'docs/assets/bubb.min.js',
69 | dest: 'dist/bubb.min.js'
70 | },
71 | {
72 | src: 'docs/assets/bubb.min.css',
73 | dest: 'dist/bubb.min.css'
74 | }
75 | ]
76 | },
77 | dev: {
78 | files: [{
79 | src: 'js/script_transpiled.js',
80 | dest: 'docs/assets/bubb.min.js'
81 | },
82 | {
83 | src: 'js/demo_transpiled.js',
84 | dest: 'docs/assets/demo.min.js'
85 | },
86 | {
87 | src: 'scss/demo.css',
88 | dest: 'docs/assets/demo.min.css'
89 | },
90 | {
91 | src: 'scss/bubb.css',
92 | dest: 'docs/assets/bubb.min.css'
93 | },
94 | {
95 | src: 'html/index.html',
96 | dest: 'docs/index.html'
97 | }]
98 | }
99 | },
100 | replace: {
101 | html: {
102 | options: {
103 | patterns: [
104 | {
105 | match: /<.+\sdelete\s.*\/.+>/g,
106 | replacement: ''
107 | },
108 | {
109 | match: /{{filesize-(.+)}}/g,
110 | replacement: function (match, key) {
111 | var fs = require('fs');
112 | function getFilesizeInBytes(filename) {
113 | const stats = fs.statSync(filename)
114 | const fileSizeInBytes = stats.size
115 | return (fileSizeInBytes/1024).toFixed(1)+' kB'
116 | }
117 | return getFilesizeInBytes('./dist/bubb.min.'+key);
118 | }
119 | },
120 | ]
121 | },
122 | files: [
123 | {expand: true, flatten: true, src: ['html/index.html'], dest: 'docs/'}
124 | ]
125 | }
126 | },
127 | clean: ['scss/style.css', 'scss/demo.css', 'scss/bubb.css', 'js/script_transpiled.js', 'js/demo_transpiled.js'],
128 | watch: {
129 | source: {
130 | files: ['scss/*.scss','html/index.html','js/script.js','js/demo.js'],
131 | tasks: ['sass','es6transpiler','copy','clean'],
132 | options: {
133 | livereload: true
134 | }
135 | }
136 | },
137 | connect: {
138 | server: {
139 | options: {
140 | port: 5000,
141 | hostname: 'localhost',
142 | base: 'docs'
143 | }
144 | }
145 | },
146 | open : {
147 | main: {
148 | path: 'http://localhost:5000',
149 | app: grunt.option('safari') ? 'Safari' : 'Google Chrome'
150 | }
151 | },
152 | shell: {
153 | git_add: {
154 | command: 'git add .'
155 | },
156 | git_commit: {
157 | command: 'git commit -m \'patch\''
158 | },
159 | git_push: {
160 | command: 'git push origin master'
161 | },
162 | npm_version: {
163 | command: 'npm version patch'
164 | },
165 | npm_publish: {
166 | command: 'npm publish'
167 | },
168 | surge: {
169 | command: 'surge'
170 | }
171 | },
172 | compress: {
173 | main: {
174 | options: {
175 | mode: 'gzip'
176 | },
177 | files: [{
178 | expand: true,
179 | cwd: 'dist/',
180 | src: ['bubb.min.css'],
181 | dest: 'dist/',
182 | extDot: 'last',
183 | ext: '.css.gz'
184 | },
185 | {
186 | expand: true,
187 | cwd: 'dist/',
188 | src: ['bubb.min.js'],
189 | dest: 'dist/',
190 | extDot: 'last',
191 | ext: '.js.gz'
192 | }]
193 | }
194 | }
195 | });
196 |
197 | };
198 |
--------------------------------------------------------------------------------
/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bubb - Euphemism for a JS tooltip
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Bubb InfotipJS
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Minimal, non-dependent , non-fancy JS infotip. No CSS needed.
47 | Infotip? Seriously? Isn't that just a euphemism for tooltip? It sure is.
48 | Still, Bubb has a couple of pros to consider.
49 |
50 | Content by config Each instance's content is referenced from a single configuration object. Plain convenience, lucid maintenance.
51 | Context menus Bubb can act as a lazy man's UI
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Basic
60 |
61 |
64 |
65 | Callback on hover
66 |
67 |
68 | Toggle
69 |
70 |
71 | Toggle remote
72 |
73 |
74 |
75 | API and options
76 |
77 |
78 |
79 |
80 | Override styling
81 | The content is targeted through bubb-content > div .
82 | The trigger element gets className .bubb (and .bubb-menu)
83 | the bubble tagname is bubb-bobb
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | Bubb InfotipCSS
93 |
97 |
98 |
99 |
100 |
101 | Pure CSS infotip, <div bubb="attribute only " />.
102 |
103 | Default
104 | Direction
105 | Delay
106 | No transitions
107 | Delayed, rounded...
108 |
109 |
110 |
111 | bubb Initiate with attribute content. Default direction south
112 | nort, east, west Change direction
113 | left, right Anchor the tip
114 | still Remove the transition
115 | delay Delay the reveal
116 | round Add some border radius
117 | large Subtly enlarge
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
130 |
131 |
134 |
135 |
136 |
137 |
138 |
139 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/js/demo.js:
--------------------------------------------------------------------------------
1 | (function(bubb, hljs){
2 |
3 | "use strict";
4 |
5 | function render_code_blocks() {
6 |
7 | // render code blocks
8 |
9 | Array.from( document.getElementsByTagName('div') ).forEach( div => {
10 |
11 | let temp = document.createElement('temp');
12 | temp.appendChild(div.cloneNode(true));
13 | let pre = document.createElement('pre'),
14 | code = document.createElement('code');
15 | pre.appendChild(code);
16 | code.innerHTML = temp.innerHTML.replace(//g,">").replace(/\=""/g,'');
17 |
18 | div.parentNode.insertBefore(pre, div.nextSibling.nextSibling);
19 |
20 | });
21 |
22 | document.querySelector('section').insertAdjacentHTML('beforeend', `
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
`); //
39 |
40 | // timeout then hide menu event info
41 |
42 | let eventsDisplay = document.getElementById('eventsDisplay');
43 |
44 | document.addEventListener('click', e => {
45 | if (e.target.id === 'eventsDisplay') return;
46 | clearTimeout(eventsDisplay.hideTimeout);
47 | eventsDisplay.hideTimeout = setTimeout(function() {
48 | eventsDisplay.innerHTML = '';
49 | }, 1500);
50 | });
51 |
52 | }
53 |
54 | function addElementsToDOM(markup) {
55 | document.querySelector('#added').insertAdjacentHTML('beforeend', markup + ' ');
56 | }
57 |
58 | function demo() {
59 |
60 | let config = {
61 | reference: 'Referenced content maintained in a separate configuration object ',
62 | pj: {
63 | vedder: 'vocals',
64 | mccready: 'guitar',
65 | gossard: 'guitar',
66 | ament: 'bass'
67 | },
68 | abbruzzese: {
69 | text: '',
70 | _: {
71 | callback: (key, item) => {
72 | item.innerHTML = 'Loading...';
73 | setTimeout(function(){
74 | item.innerHTML = designQuotes[Math.floor(Math.random() * designQuotes.length)];
75 | }, 750);
76 | },
77 | hoverCallback: true,
78 | interactive: false,
79 | transitionOff: true
80 | }
81 | },
82 | toggle: {
83 | text: ' ',
84 | _: {
85 | background: '#F7F3CE',
86 | toggle: true
87 | }
88 | },
89 | "Interstellar hootchie kootchie": {
90 | text: ' interstellar hootchie kootchie',
91 | _: {
92 | background: '#F7F3CE',
93 | color: '#4797B1'
94 | }
95 | },
96 | _nl: { text: 'North, anchor left', _: { interactive: true, direction: 'north', anchor: 'left' } },
97 | _n: { text: 'North', _: { interactive: true, direction: 'north' } },
98 | _nr: { text: 'North, anchor right', _: { interactive: true, direction: 'north', anchor: 'right' } },
99 | _el: { text: 'East, anchor left', _: { interactive: true, direction: 'east', anchor: 'left' } },
100 | _e: { text: 'East', _: { interactive: true, direction: 'east' } },
101 | _er: { text: 'East, anchor right', _: { interactive: true, direction: 'east', anchor: 'right' } },
102 | _sl: { text: 'South, anchor left', _: { interactive: true, direction: 'south', anchor: 'left' } },
103 | _s: { text: 'South', _: { interactive: true, direction: 'south' } },
104 | _sr: { text: 'South, anchor right', _: { interactive: true, direction: 'south', anchor: 'right' } },
105 | _wl: { text: 'West, anchor left', _: { interactive: true, direction: 'west', anchor: 'left' } },
106 | _w: { text: 'West', _: { interactive: true, direction: 'west' } },
107 | _wr: { text: 'West, anchor right', _: { interactive: true, direction: 'west', anchor: 'right' } },
108 | };
109 |
110 | bubb(config, (key, item) => {
111 |
112 | let eventsDisplay = document.getElementById('eventsDisplay');
113 | eventsDisplay.innerHTML = 'clicked ' + key;
114 | eventsDisplay.setAttribute('color', Array.from(item.parentNode.children).indexOf(item));
115 |
116 | console.log('clicked ' + key);
117 |
118 | });
119 |
120 | document.querySelector('#toggle').addEventListener('click', function(){
121 | bubb.toggle('toggle');
122 | }, false);
123 | document.querySelector('#toggler').addEventListener('click', function(){
124 | bubb.toggle('toggle');
125 | }, false);
126 |
127 | bubb.update('pj.mccready', 'lead guitar');
128 | bubb.add('pj.irons', 'drums');
129 | bubb.update('abbruzzese', { background: '#FFDEDE', color: '#444' });
130 |
131 | addElementsToDOM( 'Insert method 1
' );
132 |
133 | config.added_one = {
134 | text: 'config[reference] edited before refreshing bubb',
135 | _: {
136 | background: '#4797B1',
137 | color: '#fff',
138 | borderRadius: '14px',
139 | direction: 'north',
140 | anchor: 'left'
141 | }
142 | };
143 |
144 | bubb.refresh(); // finds and adds new bubbs
145 |
146 | addElementsToDOM( 'Insert method 2
' );
147 |
148 | bubb.update('added_two', 'bubb.update(reference, content) called after adding bubb to DOM');
149 | bubb.update('added_two', {
150 | width: 'section',
151 | anchor: 'left',
152 | fontSize: '23px',
153 | color: '#444',
154 | delay: 500,
155 | callback: true,
156 | class: 'tipcolor',
157 | background: 'repeating-linear-gradient(45deg, #FFDEDE, #FFDEDE 25%, #F7F3CE 25%, #F7F3CE 50%, #C5ECBE 50%, #C5ECBE 75%, #4797B1 75%, #4797B1 100%)'
158 | });
159 |
160 | document.body.insertAdjacentHTML('beforeend', ' ');
161 | //bubb.refresh();
162 |
163 | bubb.update('bubble_bobble',`
164 | Share Bubb
165 | `);
170 | bubb.update('bubble_bobble', {
171 | direction: 'west',
172 | anchor: 'right',
173 | width: '200px',
174 | class: 'share-bubb',
175 | callback: function(){
176 | console.log('thanks');
177 | }
178 | });
179 |
180 | }
181 |
182 | const display_methods = {
183 |
184 | target: '#methods',
185 | code:`// --> available methods
186 |
187 | bubb.refresh();
188 | // initialize new data-bubb elements added to DOM
189 |
190 | bubb.update(reference, content | options);
191 |
192 | bubb.update(menu-reference, options);
193 | bubb.update(menu-reference.menu-item, content);
194 |
195 | bubb.add(menu-reference.menu-item, content);
196 | bubb.remove(menu-reference.menu-item);
197 | // these methods adds or removes DOM elements
198 |
199 | `};
200 |
201 | const display_options_setup = {
202 |
203 | target: '#options_setup',
204 | code:`// --> options setup
205 |
206 | let config = {
207 | one: {
208 | text: 'content',
209 | _: {
210 | // ... one options
211 | }
212 | },
213 | two: {
214 | menu_item_1: 'content',
215 | menu_item_2: 'content',
216 | _: {
217 | // ... two options
218 | }
219 | },
220 | _: {
221 | // ... global options
222 | }
223 | }`};
224 |
225 | const display_options = {
226 |
227 | target: '#options',
228 | code:`// --> available options
229 |
230 | callback: false
231 | // function(){} overrides initial (or global) callback
232 | // boolean true adds click listener and reports to default callback
233 |
234 | transitionOff: false
235 | // boolean
236 |
237 | interactive: false
238 | // boolean, default true for menus and option callback
239 |
240 | hoverCallback: false
241 | // boolean, trigger callback on element:hover
242 |
243 | delay: false
244 | // int value, microseconds reveal delay
245 |
246 | autoHide: false
247 | // int or milliseconds
248 |
249 | toggle: false
250 | // boolean, activate tooltip with function call bubb.toggle(key)
251 |
252 | direction: false
253 | // string 'north', 'west' or 'east' (default false = 'south')
254 |
255 | autoDirection: false
256 | // boolean, screen edge proximity aware direction change
257 |
258 | anchor: false
259 | // string 'left' or 'right' (default false = 'centered')
260 |
261 | width: false
262 | // int value <= 100 (document width percentage)
263 | // css string with units (eg. '300px')
264 | // querySelector string (eg. 'section:first-of-type')
265 |
266 | borderRadius: '4px'
267 | // css string with units
268 |
269 | fontSize: '17px'
270 | // css string with units
271 |
272 | background: '#444'
273 | // css color string
274 |
275 | color: '#fff'
276 | // css color string
277 |
278 | class: false
279 | // string, className to target current bubb specifically
280 |
281 | `};
282 |
283 | const display_basic = {
284 |
285 | target: '#basic',
286 | code: `let config = {
287 | reference: 'Referenced content maintained in a separate configuration object '
288 | };
289 |
290 | bubb(config);`
291 |
292 | }
293 |
294 | const display_menu = {
295 |
296 | target: '#menu',
297 | code: `let config = {
298 | pj: {
299 | vedder: 'vocals',
300 | mccready: 'guitar',
301 | gossard: 'guitar',
302 | ament: 'bass'
303 | };
304 |
305 | bubb(config, (key, item) => {
306 | console.log('clicked ' + key);
307 | });
308 |
309 | bubb.update('pj.mccready', 'lead guitar');
310 | bubb.add('pj.irons', 'drums');
311 | // bubb.remove('pj.vedder');`
312 |
313 | }
314 |
315 | const display_opts = {
316 |
317 | target: '#opts',
318 | code: `let config = {
319 | abbruzzese: {
320 | text: '',
321 | _: {
322 | callback: function(key, item) {
323 | item.innerHTML = 'Loading...';
324 | setTimeout(function(){ // simulate async ajax
325 | item.innerHTML = designQuotes[Math.floor(Math.random() * designQuotes.length)];
326 | }, 1000);
327 | },
328 | hoverCallback: true,
329 | interactive: false,
330 | transitionOff: true
331 | }
332 | }
333 | };
334 |
335 | bubb(config);
336 |
337 | bubb.update('abbruzzese', { background: '#fad', color: '#444' });`
338 |
339 | }
340 |
341 | const display_toggle = {
342 |
343 | target: '#toggle',
344 | code: `bubb({
345 | toggle: {
346 | text: ' ',
347 | _: {
348 | background: '#F7F3CE',
349 | toggle: true
350 | }
351 | }
352 | });
353 |
354 | someElement.addEventListener('click',
355 | () => bubb.toggle('toggle') );
356 | `
357 |
358 | }
359 |
360 | const display_added = {
361 |
362 | target: '#added',
363 | code: `// method 1: update main config then refresh bubb
364 |
365 | addElementToDOM( 'Insert method 1
' ); // demo function
366 |
367 | config.added_one = {
368 | text: 'config[reference] edited before adding bubb to DOM',
369 | _: {
370 | background: '#4797B1',
371 | color: '#fff',
372 | borderRadius: '14px',
373 | direction: 'north',
374 | anchor: 'left'
375 | }
376 | };
377 |
378 | bubb.refresh();
379 |
380 |
381 |
382 | // method 2: use bubb update api
383 |
384 | addElementToDOM( 'Insert method 2
' ); // demo function
385 |
386 | bubb.update('added_two', 'bubb.update(reference, content) called after adding bubb to DOM');
387 | bubb.update('added_two', {
388 | width: 'section',
389 | anchor: 'left',
390 | fontSize: '23px',
391 | color: '#444',
392 | delay: 500,
393 | callback: true,
394 | class: 'tipcolor',
395 | background: 'repeating-linear-gradient(45deg, #FFDEDE, #FFDEDE 25%, #F7F3CE 25%, #F7F3CE 50%, #C5ECBE 50%, #C5ECBE 75%, #4797B1 75%, #4797B1 100%)'
396 | });`
397 | }
398 |
399 | const designQuotes = [
400 | "There are no bad ideas, just bad decisions.
",
401 | "It’s not in the vulgar, it’s not in the shock that one finds art. And it’s not in the excessively beautiful. It’s in between; it’s in nuance.
",
402 | "Don’t design for everyone. It’s impossible. All you end up doing is designing something that makes everyone unhappy.
",
403 | "Designers deal in ideas. They give shape to ideas that shape our world, enrich everyday experiences, and improve our lives. Where there’s confusion, designers fashion clarity; where there’s chaos, designers construct order; where there’s entropy, designers promote vitality; where there’s indifference, designers swell passion; where there’s mediocrity, designers imbue excellence; and where there’s silence, designers lend voice.
",
404 | "Stop downloading. Start uploading
",
405 | "Good design is partially creativity and innovation, but primarily knowledge and awareness.
",
406 | "Web design is responsive design. Responsive web design is web design, done right.
",
407 | "As long as there are people, there will be user experience, and user interface designers.
",
408 | "Well established hierarchies are not easily uprooted
",
409 | "Everything is possible, that’s what science is all about. No, that’s what’s being a Magical Elf is all about.
",
410 | "The designer is not always right. The researcher is not always wrong. Profit is not always the motive; market research, whatever its outcome, should never be used as a good excuse for bad design – in the same sense that good design should never be used to promote a bad product.
",
411 | "You get up early in the morning and you work all day. That’s the only secret.
",
412 | "[Designers’] primary competence lies not in the technicalities of a craft but in the mastery of a process.
",
413 | "You have to finish things — that’s what you learn from, you learn by finishing things.
",
414 | "A person tends to critique a design in one of several ways. The most common, and usually least valuable, is by gut reaction.
",
415 | "You do a disservice to your clients when you don’t fire the bad ones because you eventually provide poor service to those you don’t want to serve.
",
416 | "Think more, design less.
",
417 | "Design is how you treat your customers. If you treat them well from an environmental, emotional, and aesthetic standpoint, you’re probably doing good design.
",
418 | "Take a walk. Dance a jig. Get some sun. Don’t take yourself to serious. Cook something ethnic. Play the 3 chords you know on guitar. Go get coffee. Tell a bad joke, to yourself, and laugh. Look at the way a leaf is made. Overhear someone else’s conversation. Write it down. Remember it later. Get some sleep.
",
419 | "Innovation is seldom hindered by platform.
"
420 | ];
421 |
422 | function render_display_functions() {
423 |
424 | // display bubb js demo code
425 |
426 | function buildCodeBlock(from) {
427 |
428 | let pre = document.createElement('pre'),
429 | code = document.createElement('code');
430 | pre.appendChild(code);
431 | code.innerHTML = from.toString().replace(//g,">").replace(/\/\*\*\/[\s\S]*\/\*\*\//igm,'');
432 |
433 | return pre;
434 |
435 | }
436 |
437 | [display_basic, display_menu, display_opts, display_added, display_toggle, display_methods, display_options, display_options_setup].forEach( (obj, i) => {
438 | let block = buildCodeBlock(obj.code);
439 | document.querySelector(obj.target).appendChild(block);
440 | });
441 |
442 | // colorful code
443 |
444 | hljs.initHighlightingOnLoad();
445 | document.body.classList.add('done');
446 |
447 | }
448 |
449 | /*
450 |
451 | // mouse pointer aware directional arrow
452 |
453 | function getAngle(cx, cy, ex, ey) {
454 | var dy = ey - cy;
455 | var dx = ex - cx;
456 | var theta = Math.atan2(dy, dx);
457 | theta *= 180 / Math.PI;
458 | return theta;
459 | }
460 |
461 | function compass() {
462 |
463 | let compass = document.querySelector('#all-directions');
464 | let needle = document.querySelector('#compass');
465 |
466 | let box = needle.getBoundingClientRect();
467 |
468 | ['mousemove','mouseenter'].forEach( event => compass.addEventListener(event, function(e) {
469 |
470 | if (e.target.nodeName !== 'DIV') return;
471 |
472 | let angle = getAngle(box.left, box.top, e.clientX, e.clientY),
473 | angleStep = Math.ceil((angle+1) / 22.5) * 22.5;
474 |
475 | needle.style.transform = 'rotate(' + (angleStep + 67.5) + 'deg)';
476 |
477 | }, false));
478 |
479 | }
480 |
481 | */
482 |
483 | render_code_blocks();
484 | demo();
485 | render_display_functions();
486 |
487 | //compass();
488 |
489 | })(window.bubb, window.hljs);
490 |
--------------------------------------------------------------------------------
/js/script.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | TODO!
4 |
5 | * IMPROVE add eventEmitter
6 | - https://www.sitepoint.com/nodejs-events-and-eventemitter/
7 | - https://github.com/chrisdavies/eev
8 |
9 | * IMPROVE Implement some try/catch error handling and/or input type checks to prevent user input errors
10 |
11 | * ADD mobile version
12 | * ADD Implement dim-rest-of-page-option
13 |
14 | * TRY alternative tooltip layout (material-ui-dropdown-menu-ish) as option
15 | * TRY alternative theme (light (box-shadowed) or thin border) as option
16 |
17 | * CONSIDER Menu-style as part of package
18 |
19 | */
20 |
21 | (function(window, document) {
22 |
23 | const bubb = (config, callback) => {
24 |
25 | bubb.config = bubb.config || (typeof config === 'object' ? config : {});
26 | bubb.config._ = bubb.config._ || config._ || {};
27 | bubb.callback = bubb.callback || (typeof bubb.config._.callback === 'function' && bubb.config._.callback) || (typeof callback === 'function' && callback);
28 |
29 | // class bubb indicates the element has already been initiated/ configured
30 | let bubbs = arguments[0] === 'update' ? [arguments[1]] : Array.from( document.querySelectorAll('[data-bubb]:not(.bubb)') );
31 |
32 | if (!bubbs.length) return;
33 |
34 | !bubb._initialized && initBubb();
35 |
36 | bubbs.forEach(buildElement);
37 |
38 | }
39 |
40 | const buildElement = _trigger => {
41 |
42 | let key = _trigger.dataset.bubb.trim(),
43 | data = bubb.config[key] || key,
44 |
45 | chck = typeof data === 'object',
46 | opts = chck && data._, // config contains options _
47 | only = opts && Object.keys(data).length === 1, // options only, use key as content
48 | menu = chck && !data.hasOwnProperty('text'); // config indicates a menu
49 |
50 | only && (data.text = key);
51 |
52 | let props = !menu ? opts ? ['text'] : [false] : Object.keys(data),
53 | bindMenu = typeof bubb.callback === 'function' || typeof bubb.config._.callback === 'function',
54 | triggerPosition = window.getComputedStyle(_trigger).position;
55 |
56 | _trigger._bubb = {
57 | key: key,
58 | config: setElementConfiguration(opts),
59 | type: menu ? 'menu' : opts ? 'opts' : 'string',
60 | markup: props.reduce( buildElementMarkup.bind(this, key), '' ),
61 | bind: (menu && bindMenu) || (opts && (typeof opts.callback === 'function' || typeof opts.callback === 'boolean'))
62 | };
63 |
64 | triggerPosition && !triggerPosition.match(/absolute|fixed|relative/) && (_trigger.style.position = 'relative');
65 |
66 | _trigger.classList.add('bubb');
67 |
68 | bubb.triggers[key] = _trigger;
69 |
70 | bindElement(_trigger);
71 |
72 | };
73 |
74 | const setElementConfiguration = opts => {
75 |
76 | return availableOptions.reduce( (config, option) => {
77 | opts && opts.hasOwnProperty(option) ? config[option] = opts[option]
78 | : bubb.config._.hasOwnProperty(option) ? config[option] = bubb.config._[option]
79 | : false;
80 | return config;
81 | }, {});
82 |
83 | };
84 |
85 | const buildElementMarkup = (key, markup, prop) => {
86 |
87 | if (prop === '_') return markup;
88 |
89 | let content = prop ? bubb.config[key][prop] : bubb.config[key] || key,
90 | selector = key + (prop && prop !== 'text' ? '.' + prop : ''),
91 | attribute = ` data-bubb-value="${selector}"`;
92 |
93 | return markup += `${content}
`;
94 |
95 | };
96 |
97 | const bindElement = (_trigger) => {
98 |
99 | let bubbEvents = _trigger._bubb.config.toggle ? [] : ['mouseenter', 'mouseleave'];
100 |
101 | if (_trigger._bubb.bind) bubbEvents.push('mousedown');
102 |
103 | bubbEvents.forEach( event => _trigger.addEventListener(event, eventHandler, false) );
104 |
105 | };
106 |
107 | const configureElement = _trigger => {
108 |
109 | let trigger = _trigger._bubb,
110 | bubble = bubb._element,
111 | config = bubble._config = trigger.config;
112 |
113 | bubble.style.visibility = 'hidden';
114 |
115 | bubble._elementContent.innerHTML = trigger.markup;
116 |
117 | bubble._bind = (trigger.bind && (config.interactive !== false)) || config.toggle;
118 |
119 | bubble.className = config.class || '';
120 |
121 | setWidth(config, _trigger, bubble);
122 |
123 | trigger.type === 'menu' && _trigger.classList.add('bubb-menu');
124 |
125 | _trigger.appendChild(bubble);
126 | bubb._trigger = _trigger;
127 | bubb._visible = false;
128 |
129 | appendStyles(bubb._element, '_bubblePreactive');
130 |
131 | };
132 |
133 | const bubbShow = () => {
134 |
135 | appendStyles(bubb._element, '_bubbleActive');
136 | bubb._visible = true;
137 |
138 | };
139 |
140 | const bubbHide = (e) => {
141 |
142 | appendStyles(bubb._element, ['_bubbleInactive', '_bubblePreactive']);
143 | bubb._visible = false;
144 |
145 | };
146 |
147 | const autoDirection = (e, target) => {
148 |
149 | if (!e) return;
150 |
151 | const h = bubb._element.offsetHeight || 150;
152 | const w = bubb._element.offsetWidth || 150;
153 | const d = target._bubb.config.direction;
154 |
155 | const rect = target.getBoundingClientRect();
156 |
157 | const limits = {
158 | w: rect.left < w,
159 | e: rect.right > (window.innerWidth - w),
160 | n: rect.top < h,
161 | s: rect.bottom > (window.innerHeight - h)
162 | }
163 |
164 | if (d && (d === 'east' || d === 'west')) {
165 | bubb._autoDirection = limits.w ? 'east' : limits.e ? 'west' : false;
166 | bubb._autoAnchor = limits.n ? 'left' : limits.s ? 'right' : false;
167 | }
168 | else {
169 | bubb._autoDirection = limits.s ? 'north' : limits.n ? 'south' : false;
170 | bubb._autoAnchor = false;
171 | }
172 |
173 | return bubb._autoDirection;
174 |
175 | };
176 |
177 | const eventHandler = function(e) {
178 |
179 | if (!this._bubb) return;
180 |
181 | // ---> configure (if autoDirection in config OR trigger new element)
182 |
183 | (
184 | ((bubb.config._.autoDirection || this._bubb.config.autoDirection)
185 | && autoDirection(e, this)
186 | )
187 | || (bubb._trigger !== this)
188 | )
189 | && configureElement(this);
190 |
191 | // ---> reveal or hide
192 |
193 | window.clearTimeout(bubb._timerOn);
194 | window.clearTimeout(bubb._timerOff);
195 |
196 | e.type === 'mouseenter'
197 | ? bubb._timerOn = window.setTimeout( bubbShow, (this._bubb.config.delay | 0) || 0)
198 | : e.type !== 'mousedown' && bubbHide(e);
199 |
200 | if (this._bubb.config.autoHide) {
201 | bubb._timerOff = window.setTimeout( bubbHide, this._bubb.config.autoHide + (this._bubb.config.delay | 0) );
202 | }
203 |
204 | // ---> leave
205 |
206 | if (!this._bubb.bind || e.type === 'mouseleave') return;
207 |
208 | // ---> callback
209 |
210 | let hover = this._bubb.config.hoverCallback,
211 | bubbvalue = hover ? this.dataset.bubb : e.target.dataset.bubbValue || e.target.parentNode.dataset.bubbValue || e.target.parentNode.parentNode.dataset.bubbValue;
212 |
213 | if (!bubbvalue) return;
214 |
215 | let thiscallback = (typeof this._bubb.config.callback === 'function' && this._bubb.config.callback) || bubb.callback,
216 | item = this.querySelector(`[data-bubb-value="${this.dataset.bubb}"]`) || e.target;
217 |
218 | thiscallback(bubbvalue, item, this, e.type);
219 |
220 | }
221 |
222 | const isTrigger = (node, check) => {
223 |
224 | if (!node) return;
225 | if (node._bubb && (check ? node === bubb._trigger : true)) return node;
226 | return isTrigger(node.parentNode);
227 |
228 | };
229 |
230 | const clickOutside = function(e) {
231 |
232 | // _toggler initiated the toggle
233 |
234 | !bubb._toggler && (bubb._toggler = e.target);
235 |
236 | // avoid toggle initiator to negate the toggle
237 |
238 | if (bubb._toggler === e.target) return;
239 |
240 | // click inside; the caller is the toggled element
241 |
242 | if (isTrigger(e.target, bubb._trigger)) return;
243 |
244 | // release _toggler, remove event listener and hide bubb
245 |
246 | bubb._toggler = false;
247 | window.removeEventListener('click', clickOutside, false);
248 | eventHandler.call(bubb._trigger, {target: bubb._trigger, type: 'mouseleave'});
249 |
250 | }
251 |
252 | const toggle = () => {
253 |
254 | let element = arguments[0],
255 | _trigger = (typeof element === 'object' && element) || bubb.triggers[element];
256 |
257 | if (!_trigger || !_trigger._bubb.config.toggle) {
258 | console.error('bubb: trying to toggle a non-existing or non-toggled element');
259 | return;
260 | }
261 |
262 | //_trigger !== bubb._trigger && configureElement(_trigger);
263 |
264 | window[ (bubb._visible ? 'remove' : 'add') + 'EventListener']('click', clickOutside, false);
265 | eventHandler.call(_trigger, {target: _trigger, type: bubb._visible ? 'mouseleave' : 'mouseenter'});
266 |
267 | };
268 |
269 | const update = () => {
270 |
271 | let key = arguments[0], contentOrConfig = arguments[1];
272 |
273 | if (typeof key !== 'string' || !contentOrConfig) return;
274 |
275 | let updateOptions = typeof contentOrConfig === 'object',
276 | menu = key.split('.').reduce( (obj, val, i) => {
277 | obj[['key','val'][i]] = val;
278 | return obj;
279 | }, {}),
280 | _trigger = document.querySelector(`[data-bubb="${menu.key || key}"]`);
281 |
282 | if (!_trigger && !updateOptions) return;
283 |
284 | if (!_trigger) {
285 | console.error('bubb: trying to update a non-existing element');
286 | return;
287 | }
288 |
289 | // update added DOM element - when bubb.refresh() has not been used
290 |
291 | if (!_trigger._bubb) bubb('update', _trigger);
292 |
293 | bubb._trigger = false;
294 |
295 | // update element
296 |
297 | if (!updateOptions) {
298 | _trigger._bubb.markup = _trigger._bubb.markup.replace(new RegExp(`.*?
`), `${contentOrConfig}
`);
299 | return;
300 | }
301 |
302 | // update element config
303 |
304 | let bindDefault = typeof contentOrConfig.callback === 'boolean' && !_trigger._bubb.bind,
305 | bindSelf = typeof contentOrConfig.callback === 'function' && !_trigger._bubb.config.hasOwnProperty('callback'),
306 | bindHover = contentOrConfig.hoverCallback && !_trigger._bubb.config.hoverCallback;
307 |
308 | if (bindDefault || bindSelf || bindHover) _trigger._bubb.bind = true;
309 |
310 | if (bindDefault || bindSelf) _trigger.addEventListener( 'mousedown', eventHandler, false);
311 |
312 | Object.keys(contentOrConfig).forEach( updatedKey => {
313 | if (!~availableOptions.indexOf(updatedKey)) return;
314 | if (_trigger) _trigger._bubb.config[updatedKey] = contentOrConfig[updatedKey];
315 | else bubb.config['_'][updatedKey] = contentOrConfig[updatedKey];
316 | });
317 |
318 | // update main config
319 |
320 | updateMainConfig(key, contentOrConfig, updateOptions, _trigger);
321 |
322 | };
323 |
324 | const addOrRemove = () => {
325 |
326 | if (arguments.length === 0) {
327 | bubb();
328 | return;
329 | }
330 |
331 | let key = arguments[0], value = arguments[1],
332 | menu = key.split('.').reduce( (obj, val, i) => {
333 | obj[['key','val'][i]] = val;
334 | return obj;
335 | }, {});
336 |
337 | if (!menu.val || !bubb.config[menu.key]) return;
338 |
339 | // add menu item
340 |
341 | bubb._trigger = false;
342 |
343 | if (value && typeof value === 'string' && !bubb.config[menu.key][menu.val]) {
344 |
345 | bubb.config[menu.key][menu.val] = value;
346 |
347 | document.querySelector(`[data-bubb="${menu.key}"]`)._bubb.markup += `${value}
`;
348 | return;
349 |
350 | }
351 |
352 | // remove menu item
353 |
354 | if (!value && bubb.config[menu.key][menu.val]) {
355 |
356 | delete bubb.config[menu.key][menu.val];
357 |
358 | let _trigger = document.querySelector(`[data-bubb="${menu.key}"]`);
359 | _trigger._bubb.markup = _trigger._bubb.markup.replace(new RegExp(`.*?
`), '');
360 |
361 | }
362 |
363 | };
364 |
365 | const setWidth = (config, _trigger, _bubble) => {
366 |
367 | let input = config.width,
368 | anchor = config.anchor,
369 | direction = config.direction,
370 | sideways = direction === 'east' || direction === 'west';
371 |
372 | // '300px', '3em'
373 | if (typeof input === 'string' && parseInt(input)) {
374 | _bubble.style.width = input;
375 | return;
376 | }
377 |
378 | // 33, '33' || 'section > div'
379 | let width = (input | 0) || document.querySelector(input);
380 |
381 | if (!width) {
382 | _bubble.style.width = '100%';
383 | return;
384 | }
385 |
386 | let padding = 30,
387 | fill = typeof width === 'object',
388 | bodyw = document.body.offsetWidth,
389 | box = _trigger.getBoundingClientRect(),
390 | boxm = box.width/2,
391 | boxl = box.left,
392 | boxr = box.right,
393 | inputWidth = fill ? width.offsetWidth : (width === 100 ? bodyw - padding*2 : (width * bodyw) / 100);
394 |
395 | if (anchor || sideways) {
396 |
397 | let newWidth = !sideways && anchor
398 | ? anchor === 'left'
399 | ? bodyw - boxl - padding : anchor === 'right'
400 | ? bodyw - ( bodyw - boxr ) - padding
401 | : false
402 | : direction === 'east'
403 | ? bodyw - boxr - padding : direction === 'west'
404 | ? boxl - padding
405 | : false;
406 |
407 | if (newWidth) {
408 | _bubble.style.width = Math.min(inputWidth, newWidth) + 'px';
409 | return;
410 | }
411 |
412 | }
413 |
414 | let newWidth = Math.min(inputWidth, ((boxl + boxm > bodyw/2 ? bodyw - boxr + boxm: boxl + boxm) - padding) * 2);
415 |
416 | _bubble.style.width = newWidth + 'px';
417 | _bubble.style.left = (boxm - newWidth/2) + 'px';
418 | _bubble._elementTip.style.left = newWidth/2 + 'px';
419 |
420 | };
421 |
422 | const updateMainConfig = (key, contentOrConfig, updateOptions, _trigger) => {
423 |
424 | let typeMenu = _trigger._bubb.type === 'menu',
425 | typeOptions = _trigger._bubb.type === 'opts',
426 | keyVal = ~key.indexOf('.') && key.split('.'),
427 | menu = keyVal ? keyVal.reduce( (obj, val, i) => {
428 | obj[['key','val'][i]] = val;
429 | return obj;
430 | }, {}) : {};
431 |
432 | if (!typeMenu && !typeOptions && !updateOptions) {
433 | bubb.config[key] = contentOrConfig;
434 | return;
435 | }
436 |
437 | let prop = menu.key || key;
438 |
439 | if (!updateOptions) {
440 | bubb.config[prop][menu.val || 'text'] = contentOrConfig;
441 | return;
442 | }
443 |
444 | if (!typeMenu && !typeOptions) {
445 | bubb.config[prop] = {
446 | text: bubb.config[prop],
447 | _: contentOrConfig
448 | };
449 | _trigger._bubb.type === 'opts';
450 | return;
451 | }
452 |
453 | bubb.config[prop]['_'] = bubb.config[prop]['_'] || contentOrConfig;
454 | Object.assign(bubb.config[prop]['_'], contentOrConfig);
455 |
456 | };
457 |
458 | const styleVariables = {
459 | tipsize: '12px',
460 | offset: '.15em',
461 | distance: '20px',
462 | easing: 'cubic-bezier(0,0,0,1)',
463 | duration: '.3s',
464 | background: '#444',
465 | color: '#fff',
466 | rounding: '4px',
467 | fontsize: '17px'
468 | };
469 |
470 | const styles = {
471 | _bubble: {
472 | position: 'absolute',
473 | zIndex: '99',
474 | display: 'block',
475 | padding: '.75em .9em .85em',
476 | lineHeight: '1.1',
477 | textAlign: 'center',
478 | cursor: 'default',
479 | minWidth: '150px',
480 | width: '100%',
481 | boxSizing: 'border-box',
482 | textRendering: 'optimizeLegibility',
483 | WebkitFontSmoothing: 'antialiased',
484 | MozOsxFontSmoothing: 'grayscale',
485 | wordWrap: 'break-word',
486 | hyphens: 'auto',
487 | WebkitTouchCallout: 'none',
488 | WebkitUserSelect: 'none',
489 | KhtmlUserSelect: 'none',
490 | MozUserSelect: 'none',
491 | MsUserSelect: 'none',
492 | userSelect: 'none'
493 | },
494 | _bubbleInactive: {
495 | visibility: 'hidden',
496 | pointerEvents: 'none',
497 | opacity: '0',
498 | },
499 | _bubblePreactive: {
500 | background: styleVariables['background'],
501 | borderBottomColor: styleVariables['background'],
502 | color: styleVariables['color'],
503 | fontSize: styleVariables['fontsize'],
504 | },
505 | _bubbleActive: {
506 | transitionProperty: 'opacity, transform',
507 | transitionDuration: styleVariables['duration'],
508 | transitionTimingFunction: styleVariables['easing'],
509 | pointerEvents: 'all',
510 | opacity: '1',
511 | visibility: 'visible'
512 | },
513 | _bubbleInteractive: {
514 | position: 'absolute',
515 | zIndex: '-1',
516 | display: 'none',
517 | width: `calc( 100% + ( ${styleVariables['tipsize']} + ${styleVariables['offset']} ) * 2 )`,
518 | height: `calc( 100% + ( ${styleVariables['tipsize']} + ${styleVariables['offset']} ) * 2 )`,
519 | top: `calc( -1 * ( ${styleVariables['tipsize']} + ${styleVariables['offset']} ) )`,
520 | left: `calc( -1 * ( ${styleVariables['tipsize']} + ${styleVariables['offset']} ) )`,
521 | pointerEvents: 'all',
522 | background: 'transparent'
523 | },
524 | _bubbleTip: {
525 | position: 'absolute',
526 | width: '0',
527 | height: '0',
528 | borderLeft: `${styleVariables['tipsize']} solid transparent`,
529 | borderRight: `${styleVariables['tipsize']} solid transparent`,
530 | borderBottomWidth: styleVariables['tipsize'],
531 | borderBottomStyle: 'solid',
532 | borderBottomColor: 'inherit'
533 | }
534 | };
535 |
536 | const styleTransforms = {
537 | positive: {
538 | active: `calc( 100% + ${styleVariables['tipsize']} + ${styleVariables['offset']} )`,
539 | inactive: `calc( 100% + ${styleVariables['tipsize']} + ${styleVariables['offset']} + ${styleVariables['distance']} )`
540 | },
541 | negative: {
542 | active: `calc( -100% - ${styleVariables['tipsize']} - ${styleVariables['offset']} )`,
543 | inactive: `calc( -100% - ${styleVariables['tipsize']} - ${styleVariables['offset']} - ${styleVariables['distance']} )`
544 | }
545 | };
546 |
547 | const styleDirections = {
548 | x: {
549 | east: styleTransforms.positive,
550 | west: styleTransforms.negative
551 | },
552 | y: {
553 | south: styleTransforms.positive,
554 | north: styleTransforms.negative,
555 | }
556 | };
557 |
558 | const styleRoundings = {
559 | south: {
560 | left: [0,1,1,1],
561 | right: [1,0,1,1]
562 | },
563 | north: {
564 | left: [1,1,1,0],
565 | right: [1,1,0,1]
566 | },
567 | east: {
568 | left: [0,1,1,1],
569 | right: [1,1,1,0]
570 | },
571 | west: {
572 | left: [1,0,1,1],
573 | right: [1,1,0,1]
574 | }
575 | };
576 |
577 | const evalAnchor = (left, anchor) => anchor ? ( (anchor === 'left' && left) || (anchor === 'right' && !left) ? 0 : 'auto' ) : left ? '50%' : 'auto';
578 |
579 | const stylePositions = {
580 | south: {
581 | left: evalAnchor.bind(this, true),
582 | right: evalAnchor.bind(this, false),
583 | top: (anchor, tip) => tip ? `calc( 2px - ${styleVariables['tipsize']} )` : 'auto',
584 | bottom: (anchor, tip) => tip ? 'auto' : 0
585 | },
586 | north: {
587 | left: (anchor, tip) => evalAnchor(true, tip && anchor ? anchor === 'left' ? 'left' : 'right' : anchor),
588 | right: (anchor, tip) => evalAnchor(false, tip && anchor ? anchor === 'left' ? 'left' : 'right' : anchor),
589 | top: (anchor, tip) => tip ? 'auto' : 0,
590 | bottom: (anchor, tip) => tip ? `calc( 2px - ${styleVariables['tipsize']} )` : 'auto'
591 | },
592 | east: {
593 | left: (anchor, tip) => tip ? `calc( 2px - ${styleVariables['tipsize']} )` : 'auto',
594 | right: (anchor, tip) => tip ? 'auto' : 0,
595 | top: (anchor, tip) => tip ? evalAnchor(true, anchor) : !anchor || (anchor === 'left') ? '50%' : 'auto',
596 | bottom: (anchor, tip) => tip ? evalAnchor(false, anchor) : anchor === 'right' ? '50%' : 'auto'
597 | },
598 | west: {
599 | left: (anchor, tip) => tip ? 'auto' : 0,
600 | right: (anchor, tip) => tip ? `calc( 2px - ${styleVariables['tipsize']} )` : 'auto',
601 | top: (anchor, tip) => tip ? evalAnchor(true, anchor) : !anchor || (anchor === 'left') ? '50%' : 'auto',
602 | bottom: (anchor, tip) => tip ? evalAnchor(false, anchor) : anchor === 'right' ? '50%' : 'auto'
603 | }
604 | };
605 |
606 | const tipTransforms = {
607 | south: {
608 | center: 'translate(-50%, 0)',
609 | left: 'translate(-25%, 50%) rotate(90deg)',
610 | right: 'translate(25%, 50%) rotate(-90deg)'
611 | },
612 | north: {
613 | center: 'translate(-50%, 0) rotate(180deg)',
614 | left: 'translate(-25%, -50%) rotate(90deg)',
615 | right: 'translate(25%, -50%) rotate(-90deg)'
616 | },
617 | east: {
618 | center: 'translate(-25%, -50%) rotate(-90deg)',
619 | left: 'translate(0, 0) rotate(180deg)',
620 | right: 'translate(0, 0)'
621 | },
622 | west: {
623 | center: 'translate(25%, -50%) rotate(90deg)',
624 | left: 'translate(0, 0) rotate(180deg)',
625 | right: 'translate(0, 0)'
626 | }
627 | };
628 |
629 | const setDirectionSpecificStyles = (element, config, activeOrInactive) => {
630 |
631 | let direction = bubb._autoDirection || config.direction || 'south',
632 | anchor = bubb._autoAnchor || config.anchor;
633 |
634 | const setBubbleTransform = xy => ( typeof styleDirections[xy][direction] === 'object' && styleDirections[xy][direction][activeOrInactive] ) || ( anchor ? '0' : '-50%' );
635 |
636 | element.style.transform = `translate(${setBubbleTransform('x')}, ${setBubbleTransform('y')})`;
637 |
638 | if (activeOrInactive === 'active') return;
639 |
640 | element._elementInteractive.style.display = element._bind ? 'block' : 'none';
641 |
642 | element._elementTip.style.transform = tipTransforms[direction][anchor || 'center'];
643 |
644 | ['left', 'right', 'top', 'bottom'].forEach( position => {
645 | element.style[position] = stylePositions[direction][position](anchor);
646 | element._elementTip.style[position] = stylePositions[direction][position](anchor, true);
647 | });
648 |
649 | element.style.borderRadius = (styleRoundings[direction][anchor] || [1]).reduce( (str, chk) => { return str += (chk ? config.borderRadius || styleVariables['rounding'] : 0) + ' '; }, '');
650 |
651 | };
652 |
653 | const appendStyles = (element, keys, init) => {
654 |
655 | let config = element._config || {};
656 |
657 | keys = typeof keys === 'string' ? [keys] : keys;
658 |
659 | keys.forEach( key => {
660 |
661 | let active = key === '_bubbleActive',
662 | preactive = key === '_bubblePreactive',
663 |
664 | still = active && config.transitionOff,
665 | background = preactive && config.background,
666 | color = preactive && config.color,
667 | fontsize = preactive && config.fontSize;
668 |
669 | Object.keys(styles[key]).forEach( style => {
670 | element.style[style] = init ? styles[key][style]
671 | : style === 'transitionDuration' && still ? '0s'
672 | : (style === 'background' || style === 'borderBottomColor') && background ? background
673 | : style === 'color' && color ? color
674 | : style === 'fontSize' && fontsize ? fontsize
675 | : styles[key][style];
676 | });
677 |
678 | if (!init && (active || preactive)) setDirectionSpecificStyles(element, config, active ? 'active' : 'inactive');
679 |
680 | });
681 |
682 | };
683 |
684 | const setMethodProxies = () => {
685 |
686 | bubb.update = () => update.apply(this, arguments);
687 | bubb.add = bubb.refresh = bubb.remove = () => addOrRemove.apply(this, arguments);
688 | bubb.toggle = () => toggle.apply(this, arguments);
689 |
690 | };
691 |
692 | const createBubbElements = () => {
693 |
694 | bubb._element = document.createElement('bubb-bobb');
695 |
696 | let element = bubb._element,
697 | tagMap = {
698 | _elementInteractive: 'bubb-interactive',
699 | _elementTip: 'bubb-tip',
700 | _elementContent: 'bubb-content'
701 | };
702 |
703 | for (let tag in tagMap) {
704 | element[tag] = document.createElement(tagMap[tag]);
705 | element.appendChild(element[tag]);
706 | }
707 |
708 | appendStyles(element, ['_bubble', '_bubbleInactive'], true);
709 | appendStyles(element._elementTip, '_bubbleTip', true);
710 | appendStyles(element._elementInteractive, '_bubbleInteractive', true);
711 |
712 | bubb._den = document.createElement('bubb-den');
713 | bubb._den.style.display = 'none';
714 | bubb._den.appendChild(element);
715 |
716 | document.body.appendChild(bubb._den);
717 |
718 | };
719 |
720 | const listenToBubbEvents = () => {
721 |
722 | const hideOrKeep = () => bubb._element._bind || appendStyles(bubb._element, '_bubbleInactive', true);
723 | bubb._element.addEventListener('mouseenter', hideOrKeep, false);
724 |
725 | };
726 |
727 | const initBubb = () => {
728 |
729 | bubb._initialized = true;
730 | bubb.triggers = {};
731 |
732 | setMethodProxies();
733 | createBubbElements();
734 | listenToBubbEvents();
735 |
736 | };
737 |
738 | const availableOptions = [
739 | 'callback',
740 | 'hoverCallback',
741 | 'background',
742 | 'color',
743 | 'transitionOff',
744 | 'interactive',
745 | 'delay',
746 | 'width',
747 | 'fontSize',
748 | 'class',
749 | 'anchor',
750 | 'direction',
751 | 'borderRadius',
752 | 'autoHide',
753 | 'toggle',
754 | 'autoDirection'
755 | ];
756 |
757 | // const isMobile = (typeof window.orientation !== "undefined") || ~window.navigator.userAgent.indexOf('IEMobile') ? true : false;
758 |
759 | typeof module !== 'undefined' && module.exports ? module.exports = bubb : window.bubb = bubb;
760 |
761 | })(window, document);
762 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bubb",
3 | "description": "infotip",
4 | "version": "2.0.1",
5 | "author": "frdnrdb",
6 | "license": "MIT",
7 | "main": "./dist/bubb.min.js",
8 | "homepage": "http://bubb.surge.sh",
9 | "scripts": {
10 | "start": "grunt serve"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git@github.com:frdnrdb/bubb.git"
15 | },
16 | "dependencies": {},
17 | "devDependencies": {
18 | "fs": "0.0.2",
19 | "grunt": "^1.0.1",
20 | "grunt-contrib-clean": "^1.0.0",
21 | "grunt-contrib-compress": "^1.4.3",
22 | "grunt-contrib-connect": "^1.0.2",
23 | "grunt-contrib-copy": "^1.0.0",
24 | "grunt-contrib-cssmin": "^1.0.1",
25 | "grunt-contrib-uglify": "^1.0.1",
26 | "grunt-contrib-watch": "^1.0.0",
27 | "grunt-cssnano": "^2.1.0",
28 | "grunt-es6-transpiler": "^1.0.2",
29 | "grunt-open": "^0.2.3",
30 | "grunt-replace": "^1.0.1",
31 | "grunt-run": "^0.7.0",
32 | "grunt-sass": "^1.2.0",
33 | "grunt-shell": "^2.1.0",
34 | "load-grunt-tasks": "^3.5.2"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/scss/bubb.scss:
--------------------------------------------------------------------------------
1 | $bubb-tip-size: 10px;
2 | $bubb-offset: 4px;
3 | $bubb-transition-distance: 20px;
4 | $bubb-transition-expression: cubic-bezier(0,0,0,1);
5 | $bubb-transition-delay: .75s;
6 | $bubb-border-radius: 5px;
7 | $bubb-border-radius-round: 30px;
8 | $bubb-min-width: 9em;
9 | $bubb-font-size-large: 20px;
10 | $bubb-font-size: 17px;
11 | $bubb-background: #444;
12 | $bubb-color: #fff;
13 |
14 | @mixin bubbStyle() {
15 | padding: .75em .9em .85em;
16 | line-height: 1.1;
17 | font-size: $bubb-font-size;
18 | text-align: center;
19 | border-radius: $bubb-border-radius;
20 | background-color: $bubb-background;
21 | color: $bubb-color;
22 | min-width: $bubb-min-width;
23 | @include textRendering();
24 | @include userSelectNone();
25 | }
26 | @mixin bubbTip($width) {
27 | width: 0;
28 | height: 0;
29 | border-bottom: $width solid $bubb-background;
30 | border-right: $width solid transparent;
31 | border-left: $width solid transparent;
32 | }
33 | @mixin bubbTransition() {
34 | transition: transform .35s $bubb-transition-expression, opacity .35s $bubb-transition-expression;
35 | }
36 | @mixin textRendering() {
37 | text-rendering: optimizeLegibility;
38 | -webkit-font-smoothing: antialiased;
39 | -moz-osx-font-smoothing: grayscale;
40 | }
41 | @mixin userSelectNone() {
42 | -webkit-touch-callout: none;
43 | -webkit-user-select: none;
44 | -khtml-user-select: none;
45 | -moz-user-select: none;
46 | -ms-user-select: none;
47 | user-select: none;
48 | }
49 |
50 |
51 | // bubb bottom, bubb east
52 | $y-transform-tip: calc( #{$bubb-tip-size} + .065rem + #{$bubb-offset} + #{$bubb-transition-distance} );
53 | $y-transform-hover-tip: calc( #{$bubb-tip-size} + .065rem + #{$bubb-offset} );
54 | $y-transform: calc( 100% + #{$bubb-tip-size} + #{$bubb-offset} + #{$bubb-transition-distance} );
55 | $y-transform-hover: calc( 100% + #{$bubb-tip-size} + #{$bubb-offset} );
56 |
57 | // bubb top, bubb west
58 | $y-transform-tip-top: calc( -#{$bubb-tip-size} - .065rem - #{$bubb-offset} - #{$bubb-transition-distance} );
59 | $y-transform-hover-tip-top: calc( -#{$bubb-tip-size} - .065rem - #{$bubb-offset} );
60 | $y-transform-top: calc( -100% - #{$bubb-tip-size} - #{$bubb-offset} - #{$bubb-transition-distance} );
61 | $y-transform-hover-top: calc( -100% - #{$bubb-tip-size} - #{$bubb-offset} );
62 |
63 | @mixin bubb-base() {
64 | position: relative;
65 | &:before, &:after {
66 | position: absolute;
67 | pointer-events: none;
68 | visibility: hidden;
69 | z-index: 1;
70 | bottom: 0;
71 | opacity: 0;
72 | }
73 | &:before {
74 | content: '';
75 | @include bubbTip($bubb-tip-size);
76 | }
77 | &:after {
78 | content: attr(bubb);
79 | @include bubbStyle();
80 | }
81 | &:hover {
82 | &:before, &:after {
83 | @include bubbTransition();
84 | visibility: visible;
85 | opacity: 1;
86 | }
87 | }
88 | }
89 | @mixin bubb-center() {
90 | &:before, &:after {
91 | left: 50%;
92 | }
93 | &:before {
94 | transform: translate(-50%, $y-transform-tip);
95 | }
96 | &:after {
97 | transform: translate(-50%, $y-transform);
98 | }
99 | &:hover {
100 | &:before {
101 | transform: translate(-50%, $y-transform-hover-tip);
102 | }
103 | &:after {
104 | transform: translate(-50%, $y-transform-hover);
105 | }
106 | }
107 | }
108 | @mixin bubb-left-and-right() {
109 | &:before {
110 | transform: translate(0, $y-transform-tip);
111 | }
112 | &:after {
113 | transform: translate(0, $y-transform);
114 | }
115 | &:hover {
116 | &:before {
117 | transform: translate(0, $y-transform-hover-tip);
118 | }
119 | &:after {
120 | transform: translate(0, $y-transform-hover);
121 | }
122 | }
123 | }
124 | @mixin bubb-left() {
125 | &:before, &:after {
126 | left: 0;
127 | }
128 | &:before {
129 | border-left: none;
130 | }
131 | &:after {
132 | border-radius: 0 $bubb-border-radius $bubb-border-radius $bubb-border-radius;
133 | }
134 | }
135 | @mixin bubb-right() {
136 | &:before, &:after {
137 | left: auto;
138 | right: 0;
139 | }
140 | &:before {
141 | border-right: none;
142 | }
143 | &:after {
144 | border-radius: $bubb-border-radius 0 $bubb-border-radius $bubb-border-radius;
145 | }
146 | }
147 | @mixin bubb-east-all() {
148 | &:before, &:after {
149 | left: auto;
150 | right: 0;
151 | top: 50%;
152 | bottom: auto;
153 | }
154 | &:before {
155 | border-left: none;
156 | border-right-color: $bubb-background;
157 | }
158 | }
159 | @mixin bubb-east() {
160 | &:before {
161 | border-top: $bubb-tip-size solid transparent;
162 | border-bottom-color: transparent;
163 | transform: translate($y-transform-tip, -50%);
164 | }
165 | &:after {
166 | transform: translate($y-transform, -50%);
167 | }
168 | &:hover {
169 | &:before {
170 | transform: translate($y-transform-hover-tip, -50%);
171 | }
172 | &:after {
173 | transform: translate($y-transform-hover, -50%);
174 | }
175 | }
176 | }
177 | @mixin bubb-east-top() {
178 | &:before {
179 | border-top: none;
180 | border-bottom-color: transparent;
181 | transform: translate($y-transform-tip, 0);
182 | }
183 | &:after {
184 | border-radius: 0 $bubb-border-radius $bubb-border-radius $bubb-border-radius;
185 | transform: translate($y-transform, 0);
186 | }
187 | &:hover {
188 | &:before {
189 | transform: translate($y-transform-hover-tip, 0);
190 | }
191 | &:after {
192 | transform: translate($y-transform-hover, 0);
193 | }
194 | }
195 | }
196 | @mixin bubb-east-bottom() {
197 | &:before {
198 | border-top: $bubb-tip-size solid transparent;
199 | border-bottom: none;
200 | transform: translate($y-transform-tip, -100%);
201 | }
202 | &:after {
203 | border-radius: $bubb-border-radius $bubb-border-radius $bubb-border-radius 0;
204 | transform: translate($y-transform, -100%);
205 | }
206 | &:hover {
207 | &:before {
208 | transform: translate($y-transform-hover-tip, -100%);
209 | }
210 | &:after {
211 | transform: translate($y-transform-hover, -100%);
212 | }
213 | }
214 | }
215 | @mixin bubb-west-all() {
216 | &:before, &:after {
217 | left: 0;
218 | right: auto;
219 | top: 50%;
220 | bottom: auto;
221 | }
222 | &:before {
223 | border-right: none;
224 | border-top: $bubb-tip-size solid transparent;
225 | border-bottom-color: transparent;
226 | border-left-color: $bubb-background;
227 | }
228 | }
229 | @mixin bubb-west() {
230 | &:before {
231 | transform: translate($y-transform-tip-top, -50%);
232 | }
233 | &:after {
234 | transform: translate($y-transform-top, -50%);
235 | }
236 | &:hover {
237 | &:before {
238 | transform: translate($y-transform-hover-tip-top, -50%);
239 | }
240 | &:after {
241 | transform: translate($y-transform-hover-top, -50%);
242 | }
243 | }
244 | }
245 | @mixin bubb-west-top() {
246 | &:before {
247 | border-top: none;
248 | transform: translate($y-transform-tip-top, 0);
249 | }
250 | &:after {
251 | border-radius: $bubb-border-radius 0 $bubb-border-radius $bubb-border-radius;
252 | transform: translate($y-transform-top, 0);
253 | }
254 | &:hover {
255 | &:before {
256 | transform: translate($y-transform-hover-tip-top, 0);
257 | }
258 | &:after {
259 | transform: translate($y-transform-hover-top, 0);
260 | }
261 | }
262 | }
263 | @mixin bubb-west-bottom() {
264 | &:before {
265 | border-bottom: none;
266 | transform: translate($y-transform-tip-top, -100%);
267 | }
268 | &:after {
269 | border-radius: $bubb-border-radius $bubb-border-radius 0 $bubb-border-radius;
270 | transform: translate($y-transform-top, -100%);
271 | }
272 | &:hover {
273 | &:before {
274 | transform: translate($y-transform-hover-tip-top, -100%);
275 | }
276 | &:after {
277 | transform: translate($y-transform-hover-top, -100%);
278 | }
279 | }
280 | }
281 | @mixin bubb-north() {
282 | &:before, &:after {
283 | left: 50%;
284 | top: 0;
285 | bottom: auto;
286 | }
287 | &:before {
288 | border-bottom: none;
289 | border-top: $bubb-tip-size solid $bubb-background;
290 | border-left-color: transparent;
291 | border-right-color: transparent;
292 | transform: translate(-50%, $y-transform-tip-top);
293 | }
294 | &:after {
295 | transform: translate(-50%, $y-transform-top);
296 | }
297 | &:hover {
298 | &:before {
299 | transform: translate(-50%, $y-transform-hover-tip-top);
300 | }
301 | &:after {
302 | transform: translate(-50%, $y-transform-hover-top);
303 | }
304 | }
305 | }
306 | @mixin bubb-north-left-and-right() {
307 | &:before {
308 | transform: translate(0, $y-transform-tip-top);
309 | }
310 | &:after {
311 | transform: translate(0, $y-transform-top);
312 | }
313 | &:hover {
314 | &:before {
315 | transform: translate(0, $y-transform-hover-tip-top);
316 | }
317 | &:after {
318 | transform: translate(0, $y-transform-hover-top);
319 | }
320 | }
321 | }
322 | @mixin bubb-north-left() {
323 | &:before, &:after {
324 | left: 0;
325 | top: 0;
326 | bottom: auto;
327 | }
328 | &:before {
329 | border-left: none;
330 | }
331 | &:after {
332 | border-radius: $bubb-border-radius $bubb-border-radius $bubb-border-radius 0;
333 | }
334 | }
335 | @mixin bubb-north-right() {
336 | &:before, &:after {
337 | left: auto;
338 | right: 0;
339 | top: 0;
340 | bottom: auto;
341 | }
342 | &:before {
343 | border-right: none;
344 | }
345 | &:after {
346 | border-radius: $bubb-border-radius $bubb-border-radius 0 $bubb-border-radius;
347 | }
348 | }
349 | @mixin bubb-still() {
350 | &:hover {
351 | &:before, &:after {
352 | transition-duration: 0s;
353 | }
354 | }
355 | }
356 | @mixin bubb-delay() {
357 | &:hover {
358 | &:before, &:after {
359 | transition-delay: $bubb-transition-delay;
360 | }
361 | }
362 | }
363 | @mixin bubb-round() {
364 | &:after {
365 | border-radius: $bubb-border-radius-round !important;
366 | }
367 | &[north][left], &[east][right] {
368 | &:after {
369 | border-radius: $bubb-border-radius-round $bubb-border-radius-round $bubb-border-radius-round 0 !important;
370 | }
371 | }
372 | &[north][right], &[west][right] {
373 | &:after {
374 | border-radius: $bubb-border-radius-round $bubb-border-radius-round 0 $bubb-border-radius-round !important;
375 | }
376 | }
377 | &[left]:not([east]):not([west]):not([north]), &[east][left] {
378 | &:after {
379 | border-radius: 0 $bubb-border-radius-round $bubb-border-radius-round $bubb-border-radius-round !important;
380 | }
381 | }
382 | &[right]:not([east]):not([west]):not([north]), &[west][left] {
383 | &:after {
384 | border-radius: $bubb-border-radius-round 0 $bubb-border-radius-round $bubb-border-radius-round !important;
385 | }
386 | }
387 | }
388 | @mixin bubb-large() {
389 | &:after {
390 | font-size: $bubb-font-size-large !important;
391 | }
392 | }
393 |
394 | [bubb] {
395 | @include bubb-base();
396 | &[still] {
397 | @include bubb-still();
398 | }
399 | &[delay] {
400 | @include bubb-delay();
401 | }
402 | &[round] {
403 | @include bubb-round();
404 | }
405 | &[large] {
406 | @include bubb-large();
407 | }
408 | &:not([east]):not([west]):not([north]) {
409 | @include bubb-center();
410 | &[left], &[right] {
411 | @include bubb-left-and-right();
412 | }
413 | &[left] {
414 | @include bubb-left();
415 | }
416 | &[right] {
417 | @include bubb-right();
418 | }
419 | }
420 | &[north] {
421 | @include bubb-north();
422 | &[left], &[right] {
423 | @include bubb-north-left-and-right();
424 | }
425 | &[left] {
426 | @include bubb-north-left();
427 | }
428 | &[right] {
429 | @include bubb-north-right();
430 | }
431 | }
432 | &[east] {
433 | @include bubb-east-all();
434 | @include bubb-east();
435 | &[left] {
436 | @include bubb-east-top();
437 | }
438 | &[right] {
439 | @include bubb-east-bottom();
440 | }
441 | }
442 | &[west] {
443 | @include bubb-west-all();
444 | @include bubb-west();
445 | &[left] {
446 | @include bubb-west-top();
447 | }
448 | &[right] {
449 | @include bubb-west-bottom();
450 | }
451 | }
452 | }
453 |
--------------------------------------------------------------------------------
/scss/demo.scss:
--------------------------------------------------------------------------------
1 | $col: #444;
2 | $col2: #fad;
3 | $font-size: 1.5em;
4 | $max-width: 720px;
5 |
6 | @font-face {
7 | font-family: 'Walsheim';
8 | font-weight: bold;
9 | font-style: bold;
10 | src: url('./fonts/gt-walsheim-bold-web.ttf');
11 | }
12 | @font-face {
13 | font-family: 'Walsheim';
14 | font-weight: normal;
15 | font-style: normal;
16 | src: url('./fonts/gt-walsheim-light-web.ttf');
17 | }
18 | * {
19 | text-rendering: optimizeLegibility;
20 | -webkit-font-smoothing: antialiased;
21 | -moz-osx-font-smoothing: grayscale;
22 | }
23 |
24 | @mixin hovers() {
25 | > div {
26 | display: inline-block;
27 | font-size: 95%;
28 | margin-top: 1em;
29 | padding-bottom: 2px;
30 | border-bottom: 2px dotted $col2;
31 | cursor: help;
32 | position: static;
33 | &:hover {
34 | border-color: transparent;
35 | }
36 | }
37 | > i {
38 | &:after {
39 | content: '⟵ :hover';
40 | display: inline-block;
41 | white-space: nowrap;
42 | border-bottom: none;
43 | margin-left: 12px;
44 | font-size: 75%;
45 | opacity: .35;
46 | }
47 | &.click:after {
48 | content: '⟵ :click';
49 | }
50 | }
51 | }
52 |
53 | html {
54 | overflow-y: scroll;
55 | }
56 | body {
57 | padding: 0;
58 | margin: 0;
59 | background: #fff;
60 | color: $col;
61 | font-family: 'Walsheim', arial, sans-serif;
62 | font-weight: normal;
63 | font-size: $font-size;
64 | line-height: 1;
65 | letter-spacing: .025rem;
66 | position: relative;
67 | min-height: 100vh;
68 | box-sizing: border-box;
69 | max-width: 100vw;
70 | overflow: hidden;
71 | }
72 | header {
73 | font-size: 1em;
74 | padding: 2.5em calc( (100vw - #{$max-width}) / 2 );
75 | box-sizing: border-box;
76 | width: 100%;
77 | position: relative;
78 | display: block;
79 | background: $col;
80 | color: #fff;
81 | &:last-of-type {
82 | background: #FFDEDE;
83 | color: #444;
84 | a {
85 | color: #444;
86 | &:hover {
87 | color: #4797B1;
88 | }
89 | }
90 | }
91 | aside {
92 | margin-top: .75em;
93 | a {
94 | color: #F7F3CE;
95 | text-decoration: none;
96 | font-size: 50%;
97 | text-transform: uppercase;
98 | letter-spacing: 2px;
99 | margin: .5em .5em 0 0;
100 | &:hover {
101 | opacity: .75;
102 | }
103 | &:before {
104 | content:'';
105 | max-height: 0;
106 | overflow: hidden;
107 | display: block;
108 | }
109 | &.inline:before {
110 | content: ' | ';
111 | max-height: none;
112 | display: inline-block;
113 | opacity: .35;
114 | margin-right: .75em;
115 | font-size: 150%;
116 | position: relative;
117 | top: 4px;
118 | }
119 | &.first {
120 | display: block;
121 | padding-top: 1em;
122 | margin-left: 0;
123 | }
124 | }
125 | }
126 | i {
127 | font-style: normal;
128 | font-size: 50%;
129 | opacity: .65;
130 | position: relative;
131 |
132 | &:first-of-type {
133 | top: -.85em;
134 | }
135 | &:last-of-type {
136 | left: -1.1em;
137 | }
138 | margin-right: -.75em;
139 | }
140 | b {
141 | font-weight: bold;
142 | }
143 | span {
144 | float: right;
145 | }
146 | &:after {
147 | content:none;
148 | position:absolute;
149 | width:100vw;
150 | height:7.3em;
151 | display:block;
152 | background:rgba(0,0,0,.035);
153 | top:-3.25em;
154 | left:-7.5vw;
155 | }
156 | }
157 | code {
158 | font-size: 70%;
159 | //padding: 2em;
160 | overflow-x: scroll;
161 | &::-webkit-scrollbar {
162 | width: 0px;
163 | background: transparent;
164 | }
165 | }
166 | pre {
167 | //padding: .5em;
168 | font-size: 16px;
169 | //border: 1px solid #f0f0f0;
170 | opacity: .35;
171 | transition: opacity .25s linear;
172 | position: relative;
173 | z-index: 0;
174 | filter: grayscale(100%);
175 | &:hover {
176 | opacity: 1;
177 | filter: grayscale(0%);
178 | }
179 | }
180 | .opaque pre {
181 | opacity: 1;
182 | filter: grayscale(0%);
183 | padding: 0;
184 | border: none;
185 | font-size: 18px;
186 | }
187 |
188 | section {
189 | position: relative;
190 | max-width: $max-width;
191 | margin: 0 auto;
192 | padding: .5em 0 4em;
193 | box-sizing: border-box;
194 | display: block;
195 | width: 90%;
196 | &:first-of-type {
197 | padding: 0 0 4em;
198 | .header {
199 | font-weight: bold;
200 | width: 100%;
201 | height: 6em;
202 | line-height: 6em;
203 | text-decoration: none;
204 | background: #F7F3CE;
205 | display: block;
206 | padding: 0 5%;
207 | box-sizing: border-box;
208 | text-transform: uppercase;
209 | letter-spacing: 2px;
210 | font-size: 11px;
211 | color: #444;
212 | margin-bottom: 4em;
213 | white-space: nowrap;
214 | }
215 | }
216 | > p {
217 | font-size: 70%;
218 | line-height: 1.3;
219 | color: #888;
220 | }
221 | article {
222 | position: relative;
223 | > p {
224 | font-size: 70%;
225 | line-height: 1.3;
226 | color: #888;
227 | }
228 | > span {
229 | @include hovers();
230 | }
231 | @include hovers();
232 | }
233 | }
234 | footer {
235 | background: #F7F3CE;
236 | display: block;
237 | position: relative;
238 | height: 4.5em;
239 | width: 100%;
240 | line-height: 4.5em;
241 | a {
242 | position: absolute;
243 | top: 0;
244 | left: calc( (100vw - #{$max-width}) / 2 );
245 | text-transform: uppercase;
246 | letter-spacing: 2px;
247 | font-size: 11px;
248 | text-decoration: none;
249 | font-weight: bold;
250 | color: #444;
251 |
252 | &:before {
253 | display: inline-block;
254 | font-weight: normal;
255 | margin-right: 6px;
256 | content: 'By';
257 | }
258 | /*
259 | &:last-of-type {
260 | left: auto;
261 | right: calc( (100vw - #{$max-width}) / 2 );
262 | &:before {
263 | content: 'Download Bubb on';
264 | }
265 | }
266 | */
267 | }
268 | }
269 | #eventsDisplay {
270 | background: rgba(#0ff1ce, .7);
271 | padding: 0 7.5vw;
272 | box-sizing: border-box;
273 | line-height: 100vh;
274 | height: 100vh;
275 | width: 100vw;
276 | font-size: 65%;
277 | font-style: normal;
278 | text-transform: uppercase;
279 | text-align: center;
280 | letter-spacing: 2px;
281 | position: fixed;
282 | top: 0;
283 | right: 0;
284 | pointer-events: none;
285 | z-index: 9999999;
286 | &[color="0"] {
287 | background: rgba(#ffdede, 0.6);
288 | }
289 | &[color="1"] {
290 | background: rgba(#F7F3CE, 0.6);
291 | }
292 | &[color="2"] {
293 | background: rgba(#C5ECBE, 0.6);
294 | }
295 | &[color="3"] {
296 | background: rgba(#4797B1, 0.6);
297 | }
298 | &[color="4"] {
299 | background: rgba(#54656b, 0.6);
300 | }
301 | &:empty {
302 | visibility: hidden;
303 | }
304 | }
305 |
306 | .wait {
307 | opacity: 0;
308 | transition: opacity .25s linear;
309 | @at-root {
310 | .done & {
311 | opacity: 1;
312 | }
313 | }
314 | }
315 |
316 |
317 | section ul {
318 | font-size: 70%;
319 | list-style: none;
320 | color: #888;
321 | padding-left: 50px;
322 | line-height: 1.2;
323 | padding-top: .75em;
324 | li {
325 | margin-bottom: inherit;
326 | position: relative;
327 | &:before {
328 | position: absolute;
329 | top: .6em;
330 | height: 1px;
331 | width: 35px;
332 | left: -50px;
333 | background: #888;
334 | display: block;
335 | content: '';
336 | }
337 | u {
338 | text-decoration: none;
339 | display: block;
340 | font-weight: bold;
341 | padding-bottom: 1px;
342 | }
343 | }
344 | &:last-of-type li u {
345 | display: inline-block;
346 | margin-right: .5em;
347 | }
348 | }
349 | hr {
350 | margin: 2.5em 0 1em;
351 | opacity: .25;
352 | @at-root {
353 | section:last-of-type & {
354 | margin: 2em 0 1em;
355 | &:last-of-type {
356 | margin: 1.5em 0;
357 | }
358 | }
359 | }
360 | }
361 |
362 | .bubble-bobble {
363 | width: 44px;
364 | height: 44px;
365 | background: url(/assets/images/bubble_bobble.png);
366 | background-repeat: no-repeat;
367 | background-size: 100%;
368 | position: fixed;
369 | bottom: 1em;
370 | right: 1em;
371 | &:hover {
372 | background-position: 0 100%;
373 | }
374 | }
375 |
376 | ul.share-buttons {
377 | list-style: none;
378 | padding: 0;
379 | margin: .75em 0 0;
380 | &.device {
381 | background: #444;
382 | padding: 2em 0;
383 | text-align: center;
384 | }
385 | }
386 |
387 | ul.share-buttons li {
388 | display: inline;
389 | &:before {
390 | content: none;
391 | }
392 | &:hover img {
393 | opacity: .75;
394 | }
395 | img {
396 | min-width: 32px;
397 | min-height: 32px;
398 | max-width: 32px;
399 | margin: 0 4px;
400 | display: inline-block;
401 | }
402 | }
403 |
404 | ul.share-buttons .sr-only {
405 | position: absolute;
406 | clip: rect(1px 1px 1px 1px);
407 | clip: rect(1px, 1px, 1px, 1px);
408 | padding: 0;
409 | border: 0;
410 | height: 1px;
411 | width: 1px;
412 | overflow: hidden;
413 | }
414 |
415 |
416 |
417 | device {
418 | display: none;
419 | }
420 |
421 |
422 |
423 | @mixin noSelect() {
424 | -webkit-touch-callout: none;
425 | -webkit-user-select: none;
426 | -khtml-user-select: none;
427 | -moz-user-select: none;
428 | -ms-user-select: none;
429 | user-select: none;
430 | }
431 |
432 | #toggle {
433 | float: left;
434 | width: 50%;
435 | }
436 | #toggler {
437 | float: right;
438 | width: 50%;
439 | text-align: right;
440 | pre {
441 | visibility: hidden;
442 | }
443 | @include noSelect();
444 | }
445 | #added {
446 | display: block;
447 | width: 100%;
448 | clear: both;
449 | }
450 | #all-directions {
451 | position: absolute;
452 | top: 100px;
453 | right: 0;
454 | width: 80px;
455 | height: 78px;
456 | border: none;
457 | background: transparent;
458 | border-radius: 50%;
459 | background-image: url(images/cirque.svg);
460 | background-repeat: no-repeat;
461 | > div {
462 | display: inline-block;
463 | z-index: 1;
464 | position: absolute;
465 | width: auto;
466 | height: auto;
467 | cursor: crosshair;
468 | &:after {
469 | content: '';
470 | background: rgba(255, 255, 255, 0.8);
471 | min-width: 24px;
472 | min-height: 30px;
473 | position: absolute;
474 | transition: background .25s ease;
475 | }
476 | &:hover:after {
477 | background: rgba(255, 255, 255, 0);
478 | }
479 | &.n {
480 | &:nth-child(1) {
481 | top: 7.5%;
482 | left: 25%;
483 | &:after {
484 | transform: translate(-50%, -50%) rotate(-30deg);
485 | }
486 | }
487 | &:nth-child(2) {
488 | z-index: 9;
489 | top: 0;
490 | left: 50%;
491 | &:after {
492 | transform: translate(-50%, -50%);
493 | }
494 | }
495 | &:nth-child(3) {
496 | top: 7.5%;
497 | left: 75%;
498 | &:after {
499 | transform: translate(-50%, -50%) rotate(30deg);
500 | }
501 | }
502 | }
503 | &.e {
504 | &:nth-child(4) {
505 | right: 7.5%;
506 | top: 25%;
507 | &:after {
508 | transform: translate(-50%, -50%) rotate(60deg);
509 | }
510 | }
511 | &:nth-child(5) {
512 | right: 0;
513 | top: 50%;
514 | &:after {
515 | transform: translate(-50%, -50%) rotate(90deg);
516 | }
517 | }
518 | &:nth-child(6) {
519 | right: 7.5%;
520 | top: 75%;
521 | &:after {
522 | transform: translate(-50%, -50%) rotate(120deg);
523 | }
524 | }
525 | }
526 | &.s {
527 | &:nth-child(7) {
528 | bottom: 7.5%;
529 | left: 25%;
530 | &:after {
531 | transform: translate(-50%, -50%) rotate(30deg);
532 | }
533 | }
534 | &:nth-child(8) {
535 | z-index: 9;
536 | bottom: 0;
537 | left: 50%;
538 | &:after {
539 | transform: translate(-50%, -50%);
540 | }
541 | }
542 | &:nth-child(9) {
543 | bottom: 7.5%;
544 | left: 75%;
545 | &:after {
546 | transform: translate(-50%, -50%) rotate(-30deg);
547 | }
548 | }
549 | }
550 | &.w {
551 | &:nth-child(10) {
552 | left: 7.5%;
553 | top: 25%;
554 | &:after {
555 | transform: translate(-50%, -50%) rotate(120deg);
556 | }
557 | }
558 | &:nth-child(11) {
559 | left: 0;
560 | top: 50%;
561 | &:after {
562 | transform: translate(-50%, -50%) rotate(90deg);
563 | }
564 | }
565 | &:nth-child(12) {
566 | left: 7.5%;
567 | top: 75%;
568 | &:after {
569 | transform: translate(-50%, -50%) rotate(60deg);
570 | }
571 | }
572 | }
573 | }
574 |
575 | > span {
576 | position: absolute;
577 | width: 0;
578 | height: 0;
579 | background: pink;
580 | left: 50%;
581 | top: 50%;
582 | transform: rotate(0deg);
583 | transition: all .25s ease;
584 | opacity: 1;
585 | > i img {
586 | position: absolute;
587 | width: 35px;
588 | height: 35px;
589 | left: 50%;
590 | top: 50%;
591 | transform: translate(-50%, -50%);
592 | }
593 | /*
594 | > i {
595 | position: absolute;
596 | width:0;
597 | height: 0;
598 | border-left: 8px solid transparent;
599 | border-right: 8px solid transparent;
600 | border-bottom: 20px solid #444;
601 | border-top: none;
602 | left: 50%;
603 | top: 50%;
604 | transform: translate(-50%, -60%);
605 | }
606 | */
607 | }
608 | &:hover > span {
609 | opacity: 1;
610 | }
611 |
612 |
613 | }
614 |
615 |
616 |
617 |
618 | /*
619 | --- bubb modifiers
620 | */
621 | @mixin bubbMenuStyle() {
622 | padding: 0!important;
623 | div {
624 | padding: .75em 0;
625 | text-transform: uppercase;
626 | letter-spacing: 2px;
627 | font-size: 12px;
628 | font-weight: bold;
629 | transition: background .3s cubic-bezier(0,0,0,.75);
630 | cursor: pointer;
631 | &:first-child {
632 | padding-top: 1em;
633 | }
634 | &:hover {
635 | text-decoration: line-through;
636 | }
637 | }
638 | }
639 |
640 | .bubb {
641 | p {
642 | padding: 0;
643 | margin: 0;
644 | }
645 | b {
646 | font-weight: bold;
647 | letter-spacing: 1px;
648 | font-size: 90%;
649 | }
650 | @include noSelect();
651 | }
652 |
653 | .bubb {
654 | img {
655 | max-width: 100%;
656 | display: block;
657 | margin: 0 auto .5em;
658 | }
659 | }
660 | .bubb-menu {
661 | bubb-bobb {
662 | @include bubbMenuStyle();
663 | background: #FFDEDE!important;
664 | border-bottom-color: #FFDEDE!important;
665 | color: #444!important;
666 | div:nth-child(2) {
667 | background: #F7F3CE;
668 | color: #444;
669 | }
670 | div:nth-child(3) {
671 | background: #C5ECBE;
672 | color: #444;
673 | }
674 | div:nth-child(4) {
675 | background: #4797B1;
676 | color: #fff;
677 | }
678 | div:nth-child(5) {
679 | background: #54656b;
680 | border-radius: 0 0 4px 4px;
681 | color: #fff;
682 | }
683 | }
684 | }
685 |
686 | .tipcolor {
687 | border-bottom-color: #FFDEDE!important;
688 | }
689 |
690 |
691 |
692 |
693 |
694 |
695 | @media all and (max-width: 550px) {
696 | hide {
697 | display: none;
698 | }
699 | device {
700 | display: block;
701 | }
702 | body {
703 | font-size: 5vw;
704 | padding-bottom: 0;
705 | }
706 | header, footer {
707 | padding: 1.5em;
708 | }
709 | section {
710 | text-align: center;
711 | padding: 4em 1.5em;
712 |
713 | }
714 | section > span {
715 | display: block;
716 | }
717 | section i {
718 | display: none;
719 | }
720 | section:first-of-type .header {
721 | margin-bottom: 3em;
722 | position: relative;
723 | left: -5vw;
724 | width: 100vw;
725 | text-align: center;
726 | font-size: 50%;
727 | &.download {
728 | background: #C5ECBE;
729 | margin-bottom: 0;
730 | }
731 | &.info {
732 | background: #FFDEDE;
733 | margin-bottom: 0;
734 | }
735 | &.image {
736 | height: auto;
737 | padding: 0;
738 | margin: 0;
739 | display: block;
740 | }
741 | }
742 | section:last-of-type {
743 | padding: 1.35em 0;
744 | }
745 | section > p, section ul {
746 | font-size: 70%;
747 | }
748 | section ul {
749 | padding-left: 0;
750 | li:before {
751 | content: none;
752 | }
753 | }
754 | footer {
755 | position: static;
756 | text-align: center;
757 | padding: 2em 0 4em;
758 | line-height: 1.2;
759 | a {
760 | position: static;
761 | }
762 | }
763 | header {
764 | font-size: 1.25em;
765 |
766 | aside {
767 | a {
768 | line-height: 1.5;
769 | font-size: 35%;
770 | &.inline:before {
771 | content: '';
772 | }
773 | &.first {
774 | display: inline-block;
775 | }
776 | }
777 | }
778 | }
779 | pre {
780 | display: none;
781 | }
782 | .opaque pre {
783 | display: block;
784 | text-align: left;
785 | opacity: 1;
786 | filter: none;
787 | font-size: 80%;
788 | padding: 1em 10% 0;
789 | }
790 | .bubble-bobble {
791 | width: 32px;
792 | height: 32px;
793 | background-size: 32px;
794 | &:hover {
795 | background-position: 0 -32px;
796 | }
797 | }
798 | #all-directions {
799 | display: none;
800 | }
801 | }
802 |
--------------------------------------------------------------------------------