@xwadex/fesd 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/20240328-video4-setting.png +0 -0
  2. package/CHANGELOG.md +41 -0
  3. package/README.md +25 -0
  4. package/dist/assets/fesd-bundle.css +9 -0
  5. package/dist/assets/fesd-bundle.js +9800 -0
  6. package/dist/assets/fesd-bundle.js.map +1 -0
  7. package/index.html +25 -0
  8. package/package.json +23 -0
  9. package/prepros.config +883 -0
  10. package/src/fesd/anchor4/anchor4.js +179 -0
  11. package/src/fesd/aost4/_aost4.sass +64 -0
  12. package/src/fesd/aost4/aost4.js +138 -0
  13. package/src/fesd/article4/article4.js +280 -0
  14. package/src/fesd/article4/article4.md +1 -0
  15. package/src/fesd/category-slider/_category-slider.sass +33 -0
  16. package/src/fesd/category-slider/category-slider.js +332 -0
  17. package/src/fesd/collapse4/collapse4.js +159 -0
  18. package/src/fesd/detect4/detect4.js +70 -0
  19. package/src/fesd/dropdown4/_dropdown4.sass +185 -0
  20. package/src/fesd/dropdown4/cityData.js +830 -0
  21. package/src/fesd/dropdown4/dropdown4.js +647 -0
  22. package/src/fesd/image-preview/_image-preview.sass +26 -0
  23. package/src/fesd/image-preview/image-preview.js +209 -0
  24. package/src/fesd/image-validate/_image-validate.sass +21 -0
  25. package/src/fesd/image-validate/image-validate.js +84 -0
  26. package/src/fesd/marquee4/_marquee4.sass +45 -0
  27. package/src/fesd/marquee4/marquee4.js +371 -0
  28. package/src/fesd/modal4/_modal4.sass +134 -0
  29. package/src/fesd/modal4/modal4.js +236 -0
  30. package/src/fesd/modal4/modernModal.js +182 -0
  31. package/src/fesd/multipurpose4/_multipurpose4.sass +282 -0
  32. package/src/fesd/multipurpose4/multipurpose4.js +562 -0
  33. package/src/fesd/ripple4/_ripple4.sass +44 -0
  34. package/src/fesd/ripple4/ripple4.js +138 -0
  35. package/src/fesd/share4/share4.js +191 -0
  36. package/src/fesd/shared/shared.js +59 -0
  37. package/src/fesd/shared/utils.js +98 -0
  38. package/src/fesd/tab4/_tab4.sass +25 -0
  39. package/src/fesd/tab4/tab4.js +473 -0
  40. package/src/fesd/video4/README.md +3 -0
  41. package/src/fesd/video4/_video4.sass +117 -0
  42. package/src/fesd/video4/video4.js +237 -0
  43. package/src/fesd/video4/videoPlayer.js +195 -0
  44. package/src/fesd.js +53 -0
  45. package/src/fesd.sass +29 -0
  46. package/src/fesdDB.js +282 -0
  47. package/vite.config.js +37 -0
@@ -0,0 +1,236 @@
1
+ import ModernModal from './modernModal';
2
+ import SHARED from './../shared/shared';
3
+ import { isString, isElementExist, isFunction, getElement, getAllElements, toHTMLElement, jsonParse, warn } from './../shared/utils';
4
+
5
+ // define custom element
6
+ if (!customElements.get('modern-modal')) {
7
+ customElements.define('modern-modal', ModernModal);
8
+ }
9
+
10
+ const { MODALS } = window;
11
+
12
+ // use methods (for instance)
13
+ const useMethod = (instance, name, ...args) => {
14
+ if (!name) return;
15
+ const handler = instance[name];
16
+
17
+ if (handler && isFunction(handler)) {
18
+ handler.apply(instance, args);
19
+ }
20
+ };
21
+
22
+ // use function (for static)
23
+ const useFunction = (handler, ...args) => {
24
+ if (handler && isFunction(handler)) {
25
+ handler.apply(Modal4, args);
26
+ }
27
+ };
28
+
29
+ // dispatch modal
30
+ const dispatchModal = (options, instance) => {
31
+ const { target } = options;
32
+
33
+ const modal = MODALS[target] || getElement(options.target);
34
+
35
+ if (modal) useModal(options, instance);
36
+ if (!modal) useFetch(options, instance);
37
+ };
38
+
39
+ //
40
+ const useModal = (options, instance) => {
41
+ const { target, action, on, e } = options;
42
+
43
+ const modal = MODALS[target] || getElement(options.target);
44
+ const events = ['open', 'close', 'destroy'];
45
+
46
+ // trigger events
47
+ events.forEach(name => {
48
+ modal.once(name, () => {
49
+ if (instance) {
50
+ instance.emit(name, modal, e);
51
+ useMethod(instance, on[name], modal, e);
52
+ }
53
+ if (!instance) {
54
+ useFunction(on[name], modal);
55
+ }
56
+ });
57
+ });
58
+
59
+ // action
60
+ switch (action) {
61
+ case 'open':
62
+ modal.open();
63
+ break;
64
+ case 'close':
65
+ modal.close();
66
+ break;
67
+ case 'destroy':
68
+ modal.destroy();
69
+ break;
70
+ default:
71
+ const state = modal.getAttribute(':state');
72
+ if (state === 'close') modal.open();
73
+ if (state === 'open') modal.close();
74
+ break;
75
+ }
76
+ };
77
+
78
+ // fetch modal from files
79
+ const useFetch = async (options, instance) => {
80
+ const { target, route, container, on, e } = options;
81
+
82
+ if (!route) return warn('modal4', 'cannot find target or data-modal-route is not defined');
83
+
84
+ // trigger event handler
85
+ const useEvent = (name, ...args) => {
86
+ if (instance) {
87
+ instance.emit(name, ...args);
88
+ useMethod(instance, on[name], ...args);
89
+ }
90
+ if (!instance) {
91
+ useFunction(on[name], ...args);
92
+ }
93
+ };
94
+
95
+ // get response
96
+ fetch(route)
97
+ .then(res => {
98
+ // trigger success event
99
+ useEvent('success', e);
100
+
101
+ return res.text();
102
+ })
103
+ .then(data => {
104
+ const doms = toHTMLElement(data); /** modern-modal is initialized in this state */
105
+ const containerElement = getElement(container) || getElement(fesdDB.modal4.SETTINGS.container);
106
+
107
+ // append dom
108
+ [...doms].forEach(dom => {
109
+ containerElement.append(dom);
110
+ });
111
+
112
+ const modal = MODALS[target] || getElement(options.target);
113
+
114
+ // trigger complete event
115
+ useEvent('complete', modal);
116
+ useModal(options, instance);
117
+ })
118
+ .catch(error => {
119
+ // trigger error event
120
+ useEvent('error', error);
121
+ });
122
+ };
123
+
124
+ class Modal4 {
125
+ constructor(el, options = {}) {
126
+ this.__storage__ = {
127
+ el,
128
+ options,
129
+ };
130
+
131
+ this.#create();
132
+ }
133
+
134
+ #create() {
135
+ const { el, options } = this.__storage__;
136
+ if (!isString(el) || !isElementExist(el)) return;
137
+
138
+ const { SETTINGS, EVENTS } = fesdDB.modal4;
139
+
140
+ this.elements = getAllElements(el);
141
+ this.options = Object.assign({}, SETTINGS, options);
142
+ this.__events__ = Object.assign({}, EVENTS);
143
+
144
+ if (this.options.on) {
145
+ for (const [k, v] of Object.entries(this.options.on)) {
146
+ this.__events__[k] = [v];
147
+ }
148
+ }
149
+
150
+ this.#init();
151
+ }
152
+
153
+ #init() {
154
+ const { elements, options } = this;
155
+
156
+ elements.forEach(el => {
157
+ el.modal = {};
158
+ el.modal.instance = this;
159
+ el.modal.eventHandler = this.#trigger;
160
+ el.modal.defaultOptions = options;
161
+ el.addEventListener('click', el.modal.eventHandler);
162
+ });
163
+
164
+ this.emit('init');
165
+ }
166
+
167
+ #trigger(e) {
168
+ /** the keyword `this` in this method is pointed to the click target */
169
+ const { defaultOptions, eventHandler, instance } = this.modal;
170
+
171
+ const ons = jsonParse(this.getAttribute('data-modal-on'));
172
+ if (ons && typeof ons !== 'object') warn('modal4', 'data-modal-on must be a json string.');
173
+
174
+ const options = {
175
+ target: this.getAttribute('data-modal-target') || defaultOptions.target,
176
+ action: this.getAttribute('data-modal-action') || defaultOptions.action,
177
+ route: this.getAttribute('data-modal-route') || defaultOptions.route,
178
+ container: this.getAttribute('data-modal-container') || defaultOptions.container,
179
+ on: ons && typeof ons === 'object' ? ons : {},
180
+ e,
181
+ };
182
+
183
+ // prevent multiple click
184
+ this.removeEventListener('click', eventHandler);
185
+ setTimeout(() => {
186
+ this.addEventListener('click', eventHandler);
187
+ }, 200);
188
+
189
+ // get modal
190
+ dispatchModal(options, instance);
191
+ }
192
+
193
+ destroy() {
194
+ const { elements } = this;
195
+
196
+ elements.forEach(el => {
197
+ if (!el.modal) return;
198
+
199
+ el.removeEventListener('click', el.modal.eventHandler);
200
+ delete el.modal;
201
+ });
202
+
203
+ return this;
204
+ }
205
+
206
+ update() {
207
+ this.destroy().#create();
208
+
209
+ this.emit('update');
210
+
211
+ return this;
212
+ }
213
+
214
+ /** static method 'open' */
215
+ static open(options) {
216
+ const { SETTINGS } = fesdDB.modal4;
217
+ const openOptions = Object.assign({}, SETTINGS, { on: {} }, options);
218
+ dispatchModal(openOptions);
219
+ }
220
+
221
+ /** static method 'defineMethods' */
222
+ static defineMethods(obj) {
223
+ if (!Modal4.prototype.__methods__) Modal4.prototype.__methods__ = {};
224
+ const methods = Modal4.prototype.__methods__;
225
+
226
+ for (const [k, v] of Object.entries(obj)) {
227
+ if (isFunction(v)) methods[k] = v;
228
+ }
229
+
230
+ Object.assign(Modal4.prototype, Modal4.prototype.__methods__);
231
+ }
232
+ }
233
+
234
+ Object.assign(Modal4.prototype, SHARED);
235
+
236
+ export default Modal4;
@@ -0,0 +1,182 @@
1
+ import { OverlayScrollbars } from 'overlayscrollbars';
2
+ // self
3
+ import SHARED from './../shared/shared';
4
+ import { createUid, warn } from './../shared/utils';
5
+
6
+ if (!window.MODALS) window.MODALS = {};
7
+
8
+ const { MODALS } = window;
9
+
10
+ // create template method
11
+ const createTemplate = el => {
12
+ const { TEMPLATE } = fesdDB.modal4;
13
+ const { childDom } = el;
14
+ const container = document.createElement('div');
15
+ container.innerHTML = TEMPLATE(el.getAttribute('data-modal-template-setting'));
16
+
17
+ const content = container.querySelector('.modal-content');
18
+ [...childDom].forEach(child => {
19
+ content.append(child);
20
+ });
21
+
22
+ return container.children[0];
23
+ };
24
+
25
+ // class
26
+ class ModernModal extends HTMLElement {
27
+ constructor() {
28
+ super();
29
+ this.initialize = false;
30
+ }
31
+
32
+ static get observedAttributes() {
33
+ return [':state'];
34
+ }
35
+
36
+ attributeChangedCallback(attr, oldVal, newVal) {
37
+ switch (attr) {
38
+ case ':state':
39
+ this.#stateControl(newVal);
40
+ break;
41
+ }
42
+ }
43
+
44
+ connectedCallback() {
45
+ if (this.initialize) return;
46
+ this.initialize = true;
47
+ this.#create();
48
+ }
49
+
50
+ #create() {
51
+ const { ATTRS } = fesdDB.modal4;
52
+
53
+ this.__events__ = {};
54
+
55
+ if (!this.getAttribute(':state')) {
56
+ this.setAttribute(':state', 'close');
57
+ }
58
+
59
+ const id = this.getAttribute(ATTRS.id) || createUid();
60
+ if (!this.getAttribute(ATTRS.id)) {
61
+ warn(`modern-modal needs a ${ATTRS.id} attribute with a unique id.`);
62
+ this.setAttribute(ATTRS.id, id);
63
+ }
64
+
65
+ if (MODALS[id]) warn(`the ${ATTRS.id} "${id}" is already be used.`);
66
+ MODALS[id] = this;
67
+
68
+ this.#mount();
69
+ }
70
+
71
+ #mount() {
72
+ this.childDom = this.childNodes;
73
+ this.template = createTemplate(this);
74
+
75
+ this.innerHTML = '';
76
+ this.append(this.template);
77
+
78
+ this.#init();
79
+ }
80
+
81
+ #init() {
82
+ const scroller = this.querySelector('.modal-scroller');
83
+
84
+ this.__scroller__ = OverlayScrollbars(scroller, {
85
+ overflowBehavior: {
86
+ x: 'hidden',
87
+ },
88
+ });
89
+
90
+ this.#event();
91
+ }
92
+
93
+ #event() {
94
+ const $self = this;
95
+ const { ATTRS } = fesdDB.modal4;
96
+ const { close, destroy } = ATTRS;
97
+
98
+ $self.querySelectorAll(`[${close}]`)?.forEach(el => {
99
+ el.addEventListener('click', function () {
100
+ const closeAttr = this.getAttribute(close);
101
+ const closeTarget = closeAttr && MODALS[closeAttr] ? MODALS[closeAttr] : $self;
102
+ closeTarget.close();
103
+ });
104
+ });
105
+
106
+ $self.querySelectorAll(`[${destroy}]`)?.forEach(el => {
107
+ el.addEventListener('click', function () {
108
+ const destroyAttr = this.getAttribute(destroy);
109
+ const destroyTarget = destroyAttr && MODALS[destroyAttr] ? MODALS[destroyAttr] : $self;
110
+ destroyTarget.destroy();
111
+ });
112
+ });
113
+
114
+ $self.querySelector('[stop-propagation]')?.addEventListener('click', function (e) {
115
+ e.stopPropagation();
116
+ });
117
+ }
118
+
119
+ #stateControl(state) {
120
+ const { __scroller__ } = this;
121
+
122
+ if (state === 'open') {
123
+ this.style.display = 'block';
124
+ if (__scroller__) {
125
+ const { viewport } = __scroller__.elements();
126
+ viewport.scrollTo({
127
+ top: 0,
128
+ });
129
+ }
130
+ setTimeout(() => {
131
+ this.classList.add('show');
132
+ this.emit('open');
133
+ }, 100);
134
+ }
135
+ if (state === 'close') {
136
+ if (!this.classList.contains('show')) return;
137
+ this.classList.remove('show');
138
+ const transitionEndHandler = () => {
139
+ this.style.removeProperty('display');
140
+ this.emit('close');
141
+ this.removeEventListener('transitionend', transitionEndHandler);
142
+ };
143
+ this.addEventListener('transitionend', transitionEndHandler);
144
+ }
145
+ if (state === 'destroy') {
146
+ this.classList.remove('show');
147
+ const transitionEndHandler = () => {
148
+ const { ATTRS } = fesdDB.modal4;
149
+ const id = this.getAttribute(ATTRS.id);
150
+ this.style.removeProperty('display');
151
+ this.emit('close');
152
+ this.remove();
153
+ this.emit('destroy');
154
+ if (MODALS[id]) delete MODALS[id];
155
+ };
156
+ this.addEventListener('transitionend', transitionEndHandler);
157
+ }
158
+ }
159
+
160
+ open() {
161
+ this.setAttribute(':state', 'open');
162
+
163
+ return this;
164
+ }
165
+
166
+ close() {
167
+ this.setAttribute(':state', 'close');
168
+
169
+ return this;
170
+ }
171
+
172
+ destroy() {
173
+ this.setAttribute(':state', 'destroy');
174
+
175
+ return this;
176
+ }
177
+ }
178
+
179
+ // install on and emit
180
+ Object.assign(ModernModal.prototype, SHARED);
181
+
182
+ export default ModernModal;
@@ -0,0 +1,282 @@
1
+
2
+ // 共用分類
3
+ multipurpose-nav
4
+ display: block
5
+ &[m4-type="drag"]
6
+ .drag-container
7
+ padding: 0 20px
8
+ display: inline-flex
9
+ position: relative
10
+ max-width: 500px
11
+ // width: auto
12
+ width: 100%
13
+ text-align: center
14
+ &.scrollable
15
+ padding: 0 20px
16
+ .navigation
17
+ display: block
18
+ .navigation
19
+ // display: none
20
+ .button
21
+ display: flex
22
+ align-items: center
23
+ position: absolute
24
+ top: 50%
25
+ z-index: 2
26
+ background-color: #fff
27
+ width: 30px
28
+ height: 100%
29
+ opacity: 1
30
+ transition: opacity .6s
31
+ &.prev
32
+ left: 5px
33
+ transform: translate3d(-50%, -50%, 0)
34
+ div
35
+ &::before
36
+ border-top: 2px solid #333
37
+ border-left: 2px solid #333
38
+ transform: rotate(-45deg) translate3d(1px, 1px, 0)
39
+ &.hide
40
+ opacity: .3
41
+ pointer-events: none
42
+ div
43
+ &::before
44
+ border-top: 2px solid #666
45
+ border-left: 2px solid #666
46
+ &.next
47
+ right: 5px
48
+ transform: translate3d(50%, -50%, 0)
49
+ div
50
+ &::before
51
+ border-top: 2px solid #333
52
+ border-right: 2px solid #333
53
+ transform: rotate(45deg) translate3d(-1px, 1px, 0)
54
+ &.hide
55
+ opacity: .3
56
+ pointer-events: none
57
+ div
58
+ &::before
59
+ border-top: 2px solid #666
60
+ border-right: 2px solid #666
61
+ div
62
+ display: flex
63
+ align-items: center
64
+ justify-content: center
65
+ width: 35px
66
+ height: 35px
67
+ cursor: pointer
68
+ &::before
69
+ display: block
70
+ width: 8px
71
+ height: 8px
72
+ content: ''
73
+ .wrapper
74
+ padding: 2px 0 // 2px 英文字行高, 防止 g y 等被咖掉
75
+ display: grid
76
+ grid-auto-flow: column
77
+ grid-auto-columns: max-content
78
+ grid-template-rows: none
79
+ justify-content: flex-start
80
+
81
+ gap: 10px 25px !important
82
+
83
+ overflow: auto hidden
84
+ overscroll-behavior-inline: contain
85
+ scroll-snap-type: inline mandatory
86
+ scroll-padding-inline: 85px
87
+
88
+ -webkit-scrollbar-width: none
89
+ -moz-scrollbar-width: none
90
+ -ms-scrollbar-width: none
91
+ scrollbar-width: none
92
+ &::-webkit-scrollbar
93
+ display: none
94
+
95
+ &.center
96
+ mask-image: linear-gradient(270deg, rgba(#fff, 0), #fff 20%, #fff 80%, rgba(#fff, 0))
97
+ &.start
98
+ mask-image: linear-gradient(270deg, rgba(#fff, 0), #fff 20%, #fff)
99
+ &.end
100
+ mask-image: linear-gradient(90deg, rgba(#fff, 0), #fff 20%, #fff)
101
+ .item
102
+ position: relative
103
+ flex-shrink: 0
104
+ text-align: center
105
+ cursor: pointer
106
+ &.active
107
+ .category
108
+ &::after
109
+ width: 100%
110
+ .category
111
+ display: block
112
+ position: relative
113
+ padding: 10px 0
114
+ color: #000
115
+ font-size: 14px
116
+ font-weight: 700
117
+ line-height: 1.5
118
+ letter-spacing: .5px
119
+ text-decoration: none
120
+ &::after
121
+ display: block
122
+ position: absolute
123
+ bottom: 0
124
+ right: 0
125
+ background-color: #666
126
+ width: 0
127
+ height: 4px
128
+ transition: width .2s, background .2s
129
+ content: ''
130
+ &[m4-type="collapse"]
131
+ position: relative
132
+ &.expand
133
+ .drag-container
134
+ .wrapper
135
+ display: none
136
+ .collapse-placeholder
137
+ display: block
138
+ .open-collapse
139
+ &::before
140
+ transform: scale(-1)
141
+ .collapse-container
142
+ opacity: 1
143
+ transform: translateZ(0)
144
+ pointer-events: auto
145
+ .drag-container
146
+ display: inline-flex
147
+ align-items: center
148
+ position: relative
149
+ max-width: 500px
150
+ width: 100%
151
+ text-align: center
152
+ .wrapper
153
+ padding: 2px 0 // 2px 英文字行高, 防止 g y 等被咖掉
154
+ display: grid
155
+ grid-auto-flow: column
156
+ grid-auto-columns: max-content
157
+ grid-template-rows: none
158
+ justify-content: flex-start
159
+
160
+ gap: 10px 25px !important
161
+
162
+ overflow: auto hidden
163
+ overscroll-behavior-inline: contain
164
+ scroll-snap-type: inline mandatory
165
+ scroll-padding-inline: 85px
166
+
167
+ -webkit-scrollbar-width: none
168
+ -moz-scrollbar-width: none
169
+ -ms-scrollbar-width: none
170
+ scrollbar-width: none
171
+ &::-webkit-scrollbar
172
+ display: none
173
+
174
+ &.center
175
+ mask-image: linear-gradient(270deg, rgba(#fff, 0), #fff 20%, #fff 80%, rgba(#fff, 0))
176
+ &.start
177
+ mask-image: linear-gradient(270deg, rgba(#fff, 0), #fff 20%, #fff)
178
+ &.end
179
+ mask-image: linear-gradient(90deg, rgba(#fff, 0), #fff 20%, #fff)
180
+ .item
181
+ position: relative
182
+ flex-shrink: 0
183
+ text-align: center
184
+ cursor: pointer
185
+ &.active
186
+ .category
187
+ &::after
188
+ width: 100%
189
+ .category
190
+ display: block
191
+ position: relative
192
+ padding: 10px 0
193
+ color: #000
194
+ font-size: 14px
195
+ font-weight: 700
196
+ line-height: 1.5
197
+ letter-spacing: .5px
198
+ text-decoration: none
199
+ &::after
200
+ display: block
201
+ position: absolute
202
+ bottom: 0
203
+ right: 0
204
+ background-color: #666
205
+ width: 0
206
+ height: 4px
207
+ transition: width .2s, background .2s
208
+ content: ''
209
+ .collapse-placeholder
210
+ padding: 12px 0
211
+ display: none
212
+ width: 100%
213
+ font-size: 14px
214
+ font-weight: 700
215
+ line-height: 1.5
216
+ letter-spacing: .5px
217
+ text-align: left
218
+ user-select: none
219
+ .open-collapse
220
+ margin-left: 10px
221
+ padding-left: 10px
222
+ order: 1
223
+ display: flex
224
+ align-items: center
225
+ justify-content: center
226
+ flex-shrink: 0
227
+ border-left: 1px solid rgba(#000, .3)
228
+ width: 32px
229
+ height: 25px
230
+ cursor: pointer
231
+ &::before
232
+ width: 0
233
+ height: 0
234
+ border-style: solid
235
+ border-width: 6px 5px 0 5px
236
+ border-color: #000 transparent transparent transparent
237
+ content: ''
238
+ .collapse-container
239
+ padding: 20px
240
+ position: absolute
241
+ left: 0
242
+ z-index: 1
243
+ background-color: #fff
244
+ border-radius: 5px
245
+ border: 1px solid rgba(#000, .2)
246
+ max-width: 500px
247
+ // width: auto
248
+ width: 100%
249
+ opacity: 0
250
+ transform: translate3d(0, -10px, 0)
251
+ pointer-events: none
252
+ transition: opacity .3s, transform .3s
253
+ .wrapper
254
+ display: flex
255
+ flex-wrap: wrap
256
+ margin: 0 -10px -10px
257
+ .item
258
+ margin: 0 10px 10px
259
+ padding: 5px 20px
260
+ position: relative
261
+ flex-shrink: 0
262
+ border: 1px solid rgba(#000, .2)
263
+ border-radius: 500px
264
+ text-align: center
265
+ cursor: pointer
266
+ &:hover
267
+ background-color: #000
268
+ .category
269
+ color: #fff
270
+ &.active
271
+ background-color: #000
272
+ .category
273
+ color: #fff
274
+ .category
275
+ display: block
276
+ position: relative
277
+ color: #000
278
+ font-size: 14px
279
+ font-weight: 700
280
+ line-height: 1.5
281
+ letter-spacing: .5px
282
+ text-decoration: none