├── .no-sublime-package ├── .gitignore ├── .gitattributes ├── .npmrc ├── screenshot.gif ├── PostCSSSorting.sublime-commands ├── messages ├── 1.1.0.txt ├── 1.2.0.txt ├── 1.4.0.txt ├── 2.1.1.txt ├── 4.0.0.txt ├── 1.3.0.txt ├── 2.1.0.txt ├── 1.5.0.txt ├── 2.0.0.txt └── 3.0.0.txt ├── Default (OSX).sublime-keymap ├── Default (Linux).sublime-keymap ├── Default (Windows).sublime-keymap ├── package.json ├── .editorconfig ├── messages.json ├── PostCSSSorting.sublime-settings ├── sorting.js ├── node_bridge.py ├── LICENSE.md ├── Main.sublime-menu ├── README.md ├── CHANGELOG.md ├── PostCSSSorting.py └── test.pcss /.no-sublime-package: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !node_modules 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hudochenkov/sublime-postcss-sorting/HEAD/screenshot.gif -------------------------------------------------------------------------------- /PostCSSSorting.sublime-commands: -------------------------------------------------------------------------------- 1 | [{ 2 | "caption": "Run PostCSS Sorting", 3 | "command": "postcsssorting" 4 | }] 5 | -------------------------------------------------------------------------------- /messages/1.1.0.txt: -------------------------------------------------------------------------------- 1 | Version 1.1.0 2 | 3 | * Update postcss-sorting to 1.1.0 4 | * Support for SCSS files to sort 5 | -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["ctrl+shift+s"], 4 | "command": "postcsssorting" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["ctrl+alt+shift+s"], 4 | "command": "postcsssorting" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["ctrl+alt+shift+s"], 4 | "command": "postcsssorting" 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /messages/1.2.0.txt: -------------------------------------------------------------------------------- 1 | Version 1.2.0 2 | 3 | * Added options for auto-sorting on save (disabled by defaut): 4 | 5 | { 6 | "sort-on-save": false 7 | } 8 | -------------------------------------------------------------------------------- /messages/1.4.0.txt: -------------------------------------------------------------------------------- 1 | Version 1.4.0 2 | 3 | * Added `empty-lines-between-media-rules` option which set a number of empty lines between nested media rules. 4 | -------------------------------------------------------------------------------- /messages/2.1.1.txt: -------------------------------------------------------------------------------- 1 | Version 2.1.1 2 | 3 | * Fixed a regression when `emptyLineBefore` wasn't working properly when `declaration-empty-line-before` wasn't set. 4 | -------------------------------------------------------------------------------- /messages/4.0.0.txt: -------------------------------------------------------------------------------- 1 | Version 4.0.0 2 | 3 | * Updated to postcss-sorting@4.0.0. Read more about release: https://github.com/hudochenkov/postcss-sorting/releases/tag/4.0.0 4 | -------------------------------------------------------------------------------- /messages/1.3.0.txt: -------------------------------------------------------------------------------- 1 | Version 1.3.0 2 | 3 | * Added `empty-lines-between-children-rules` option which set a number of empty lines between nested children rules. (thanks, @scalder27) 4 | -------------------------------------------------------------------------------- /messages/2.1.0.txt: -------------------------------------------------------------------------------- 1 | Version 2.1.0 2 | 3 | * Update to postcss-sorting@2.1.0 https://github.com/hudochenkov/postcss-sorting/releases/tag/2.1.0 4 | * Add support for `n` Node.js version manager 5 | -------------------------------------------------------------------------------- /messages/1.5.0.txt: -------------------------------------------------------------------------------- 1 | Version 1.5.0 2 | 3 | * Added `preserve-empty-lines-between-children-rules`, which preserve empty lines between children rules and preserve empty lines for comments between children rules. 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "get-stdin": "^6.0.0", 4 | "postcss": "^7.0.0", 5 | "postcss-scss": "^2.0.0", 6 | "postcss-sorting": "^4.0.0" 7 | }, 8 | "private": true 9 | } 10 | -------------------------------------------------------------------------------- /messages/2.0.0.txt: -------------------------------------------------------------------------------- 1 | Version 2.0.0 2 | 3 | BREAKING CHANGES RELEASE! 4 | 5 | * Update to postcss-sorting@2.0.1 6 | * Config for 2.0.0 is incompatible with previous configs. Please read migration guide and docs https://github.com/hudochenkov/postcss-sorting#migration-from-1x 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [package.json] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.0": "messages/1.1.0.txt", 3 | "1.2.0": "messages/1.2.0.txt", 4 | "1.3.0": "messages/1.3.0.txt", 5 | "1.4.0": "messages/1.4.0.txt", 6 | "1.5.0": "messages/1.5.0.txt", 7 | "2.0.0": "messages/2.0.0.txt", 8 | "2.1.0": "messages/2.1.0.txt", 9 | "2.1.1": "messages/2.1.1.txt", 10 | "3.0.0": "messages/3.0.0.txt", 11 | "4.0.0": "messages/4.0.0.txt" 12 | } 13 | -------------------------------------------------------------------------------- /PostCSSSorting.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // Please visit https://github.com/hudochenkov/postcss-sorting#options for all plugin options 3 | // "order": [ 4 | // "custom-properties", 5 | // "declarations" 6 | // ], 7 | // "properties-order": [ 8 | // "position", 9 | // "display", 10 | // ], 11 | // "unspecified-properties-position": "bottom", 12 | 13 | // Set true to sort on save every file with supported syntax 14 | "sort-on-save": false 15 | } 16 | -------------------------------------------------------------------------------- /messages/3.0.0.txt: -------------------------------------------------------------------------------- 1 | Version 3.0.0 2 | 3 | BREAKING CHANGES! 4 | 5 | * Update to postcss-sorting@3.1.0 6 | * Config for 3.0.0 might be partially incompatible with 2.0.0 config. Please read migration guide and postcss-sorting release notes: 7 | * Release notes: https://github.com/hudochenkov/postcss-sorting/releases/tag/3.0.0 8 | * Migration guide from 2.x: https://github.com/hudochenkov/postcss-sorting#migration-from-2x 9 | * Added support for config in `postcss-sorting.json` and `.postcss-sorting.json`, which could be located in the root folder of a project. 10 | -------------------------------------------------------------------------------- /sorting.js: -------------------------------------------------------------------------------- 1 | const getStdin = require('get-stdin'); 2 | const postcss = require('postcss'); 3 | const scss = require('postcss-scss'); 4 | const sorting = require('postcss-sorting'); 5 | 6 | getStdin().then(data => { 7 | const opts = JSON.parse(process.argv[2]); 8 | 9 | postcss(sorting(opts)) 10 | .process(data, { 11 | syntax: scss, 12 | from: undefined, 13 | }) 14 | .then(result => { 15 | process.stdout.write(result.css); 16 | }) 17 | .catch(err => { 18 | if (err.name === 'CssSyntaxError') { 19 | err.message += `\n${err.showSourceCode()}`; 20 | } 21 | 22 | console.error(err.message.replace(':', '')); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /node_bridge.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import subprocess 4 | 5 | IS_OSX = platform.system() == 'Darwin' 6 | IS_WINDOWS = platform.system() == 'Windows' 7 | 8 | def node_bridge(data, bin, args=[]): 9 | env = None 10 | startupinfo = None 11 | if IS_OSX: 12 | # GUI apps in OS X doesn't contain .bashrc/.zshrc set paths 13 | env = os.environ.copy() 14 | env['PATH'] += os.path.expanduser('~/n/bin') 15 | env['PATH'] += ':/usr/local/bin' 16 | if IS_WINDOWS: 17 | startupinfo = subprocess.STARTUPINFO() 18 | startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 19 | try: 20 | p = subprocess.Popen(['node', bin] + args, 21 | stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, 22 | env=env, startupinfo=startupinfo) 23 | except OSError: 24 | raise Exception('Couldn\'t find Node.js. Make sure it\'s in your $PATH by running `node -v` in your command-line.') 25 | stdout, stderr = p.communicate(input=data.encode('utf-8')) 26 | stdout = stdout.decode('utf-8') 27 | stderr = stderr.decode('utf-8') 28 | if stderr: 29 | raise Exception('Error: %s' % stderr) 30 | else: 31 | return stdout 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2015-present Aleks Hudochenkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "tools", 4 | "children": [ 5 | { 6 | "id": "postcsssorting", 7 | "caption": "Run PostCSS Sorting", 8 | "command": "postcsssorting" 9 | } 10 | ] 11 | }, 12 | { 13 | "caption": "Preferences", 14 | "mnemonic": "n", 15 | "id": "preferences", 16 | "children": [ 17 | { 18 | "caption": "Package Settings", 19 | "mnemonic": "P", 20 | "id": "package-settings", 21 | "children": [ 22 | { 23 | "caption": "PostCSS Sorting", 24 | "children": [ 25 | { 26 | "caption": "Settings", 27 | "command": "edit_settings", 28 | "args": { 29 | "base_file": "${packages}/PostCSS Sorting/PostCSSSorting.sublime-settings", 30 | "default": "${packages}/User/PostCSSSorting.sublime-settings" 31 | } 32 | }, 33 | { 34 | "caption": "Key Bindings", 35 | "command": "edit_settings", 36 | "args": { 37 | "base_file": "${packages}/PostCSS Sorting/Default ($platform).sublime-keymap", 38 | "default": "${packages}/User/Default ($platform).sublime-keymap" 39 | } 40 | } 41 | ] 42 | } 43 | ] 44 | } 45 | ] 46 | } 47 | ] 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sublime-postcss-sorting 2 | 3 | Sublime Text plugin to sort CSS rules content with specified order. Powered by [postcss-sorting](https://github.com/hudochenkov/postcss-sorting). 4 | 5 | ![](screenshot.gif) 6 | 7 | Works only with CSS, SCSS and PostCSS. 8 | 9 | 10 | ## Install 11 | 12 | Install `PostCSS Sorting` with [Package Control](https://packagecontrol.io/) and restart Sublime Text. 13 | 14 | **You need to have [Node.js](https://nodejs.org) 6+ installed.** 15 | Make sure it's in your $PATH by running `node -v` in your command-line. 16 | On OS X you need to make sure it's in `/usr/local/bin` or symlink it there. 17 | 18 | 19 | ## Getting started 20 | 21 | In a CSS or PostCSS file, open the Command Palette (Cmd + Shift + P (OS X), Ctrl + Shift + P (Windows/Linux)) and choose `Run PostCSS Sorting`. You can alternatively create one or more selections before running the command to only sort those parts. It should be whole rule. 22 | 23 | There is keyboard shortcut also: Ctrl + Shift + S (OS X), Ctrl + Alt + Shift + S (Windows/Linux). 24 | 25 | 26 | ### Options 27 | 28 | *(Preferences → Package Settings → PostCSS Sorting → Settings)* 29 | 30 | You can specify sort order and other options. See the [postcss-sorting documentation](https://github.com/hudochenkov/postcss-sorting#options) for all options. 31 | 32 | 33 | #### Default 34 | 35 | ```json 36 | { 37 | "sort-on-save": false 38 | } 39 | ``` 40 | 41 | 42 | ### Project settings 43 | 44 | You can override the default and user settings for individual projects. Just add an `"PostCSSSorting"` object to the `"settings"` object in the project's `.sublime-project` file containing your [project specific settings](http://www.sublimetext.com/docs/3/projects.html). 45 | 46 | Example: 47 | 48 | ```json 49 | { 50 | "settings": { 51 | "PostCSSSorting": { 52 | "properties-order": ["padding", "margin"], 53 | "sort-on-save": true 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | 60 | ## Acknowledgements 61 | 62 | This plugin is based on the [sublime-autoprefixer plugin](https://github.com/sindresorhus/sublime-autoprefixer) by Sindre Sorhus. 63 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](https://semver.org/). 4 | 5 | ## 4.0.0 6 | 7 | * Updated to postcss-sorting@4.0.0. Read more about release: https://github.com/hudochenkov/postcss-sorting/releases/tag/4.0.0 8 | 9 | ## 3.0.1 10 | 11 | * Fix for a crash if file was opened outside of a project. 12 | 13 | ## 3.0.0 14 | 15 | BREAKING CHANGES! 16 | 17 | * Update to postcss-sorting@3.1.0 18 | * Config for 3.0.0 might be partially incompatible with 2.0.0 config. Please read migration guide and postcss-sorting release notes: 19 | * Release notes: https://github.com/hudochenkov/postcss-sorting/releases/tag/3.0.0 20 | * Migration guide from 2.x: https://github.com/hudochenkov/postcss-sorting#migration-from-2x 21 | * Added support for config in `postcss-sorting.json` and `.postcss-sorting.json`, which could be located in the root folder of a project. 22 | 23 | ## 2.1.1 24 | 25 | * Fixed a regression when `emptyLineBefore` wasn't working properly when `declaration-empty-line-before` wasn't set. 26 | 27 | ## 2.1.0 28 | 29 | * Update to postcss-sorting@2.1.0 https://github.com/hudochenkov/postcss-sorting/releases/tag/2.1.0 30 | * Add support for `n` Node.js version manager 31 | 32 | ## 2.0.0 33 | 34 | * Update to postcss-sorting@2.0.1 35 | * Config for 2.0.0 is incompatible with previous configs. Please read migration guide and docs https://github.com/hudochenkov/postcss-sorting#migration-from-1x 36 | 37 | ## 1.7.0 38 | * Update to postcss-sorting@1.7.0 39 | 40 | ## 1.6.1 41 | * Update to postcss-sorting@1.6.1 42 | 43 | ## 1.6.0 44 | * Update to postcss-sorting@1.6.0 45 | * Update dependencies 46 | 47 | ## 1.5.1 48 | * Update to postcss-sorting@1.4.1 49 | 50 | ## 1.5.0 51 | * Added `preserve-empty-lines-between-children-rules`, which preserve empty lines between children rules and preserve empty lines for comments between children rules. 52 | 53 | ## 1.4.1 54 | * Update to postcss-sorting@1.3.1 55 | 56 | ## 1.4.0 57 | * Added `empty-lines-between-media-rules` option which set a number of empty lines between nested media rules. 58 | 59 | ## 1.3.3 60 | * Update to postcss-sorting@1.2.3 61 | 62 | ## 1.3.2 63 | * Update to postcss-sorting@1.2.2 64 | 65 | ## 1.3.1 66 | * Update to postcss-sorting@1.2.1 67 | 68 | ## 1.3.0 69 | * Added `empty-lines-between-children-rules` option which set a number of empty lines between nested children rules. (thanks, @scalder27) 70 | 71 | ## 1.2.0 72 | * Added options for auto-sorting on save (disabled by defaut): 73 | 74 | ```json 75 | { 76 | "sort-on-save": false 77 | } 78 | ``` 79 | 80 | ## 1.1.0 81 | * Update postcss-sorting to 1.1.0 82 | * Support for SCSS files to sort 83 | 84 | ## 1.0.1 85 | * Fix package folder name in menu paths. 86 | 87 | ## 1.0.0 88 | * Initial release. 89 | -------------------------------------------------------------------------------- /PostCSSSorting.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import json 4 | from os.path import dirname, realpath, join, basename, splitext 5 | from os import path 6 | from .node_bridge import node_bridge 7 | 8 | # monkeypatch `Region` to be iterable 9 | sublime.Region.totuple = lambda self: (self.a, self.b) 10 | sublime.Region.__iter__ = lambda self: self.totuple().__iter__() 11 | 12 | BIN_PATH = join(sublime.packages_path(), dirname(realpath(__file__)), 'sorting.js') 13 | CONFIG_NAMES = ['.postcss-sorting.json', 'postcss-sorting.json'] 14 | 15 | def local_postcss_settings(): 16 | if sublime.active_window().project_data(): 17 | for folder in sublime.active_window().project_data()['folders']: 18 | for config_name in CONFIG_NAMES: 19 | config_file = join(folder['path'], config_name) 20 | if path.isfile(config_file): 21 | with open(config_file) as data_file: 22 | return json.load(data_file) 23 | 24 | return {} 25 | 26 | def sublime_project_settings(view): 27 | settings = view.settings().get('PostCSSSorting') 28 | 29 | if settings is None: 30 | settings = {} 31 | 32 | return settings 33 | 34 | def get_setting(view, key): 35 | setting = local_postcss_settings().get(key) 36 | 37 | if setting is None: 38 | setting = sublime_project_settings(view).get(key) 39 | 40 | if setting is None: 41 | setting = sublime.load_settings('PostCSSSorting.sublime-settings').get(key) 42 | 43 | return setting 44 | 45 | def is_supported_syntax(view): 46 | syntax = splitext(basename(view.settings().get('syntax')))[0] 47 | 48 | return syntax in ('CSS', 'PostCSS', 'SCSS') 49 | 50 | class PostcsssortingCommand(sublime_plugin.TextCommand): 51 | def run(self, edit): 52 | if not self.has_selection(): 53 | region = sublime.Region(0, self.view.size()) 54 | originalBuffer = self.view.substr(region) 55 | processed = self.sorting(originalBuffer) 56 | 57 | if processed: 58 | self.view.replace(edit, region, processed) 59 | 60 | return 61 | 62 | for region in self.view.sel(): 63 | 64 | if region.empty(): 65 | continue 66 | 67 | originalBuffer = self.view.substr(region) 68 | processed = self.sorting(originalBuffer) 69 | 70 | if processed: 71 | self.view.replace(edit, region, processed) 72 | 73 | def sorting(self, data): 74 | sorting_options = {} 75 | 76 | if get_setting(self.view, 'order'): 77 | sorting_options['order'] = get_setting(self.view, 'order') 78 | 79 | if get_setting(self.view, 'properties-order'): 80 | sorting_options['properties-order'] = get_setting(self.view, 'properties-order') 81 | 82 | if get_setting(self.view, 'unspecified-properties-position'): 83 | sorting_options['unspecified-properties-position'] = get_setting(self.view, 'unspecified-properties-position') 84 | 85 | try: 86 | return node_bridge(data, BIN_PATH, [json.dumps(sorting_options)]) 87 | except Exception as e: 88 | sublime.error_message('PostCSS Sorting\n%s' % e) 89 | 90 | def has_selection(self): 91 | for sel in self.view.sel(): 92 | start, end = sel 93 | 94 | if start != end: 95 | return True 96 | 97 | return False 98 | 99 | class PostcsssortingPreSaveCommand(sublime_plugin.EventListener): 100 | def on_pre_save(self, view): 101 | if get_setting(view, 'sort-on-save') is True and is_supported_syntax(view): 102 | view.run_command('postcsssorting') 103 | -------------------------------------------------------------------------------- /test.pcss: -------------------------------------------------------------------------------- 1 | div p em { 2 | /* upline comment 1 */ 3 | /* upline comment 2 */ 4 | font-style: italic; 5 | border-bottom: 1px solid red; /* trololo 1 */ /* trololo 2 */ 6 | } 7 | 8 | .block { 9 | position: absolute; 10 | 11 | span { 12 | display: inline-block; 13 | } 14 | 15 | width: 50%; 16 | 17 | &__element { 18 | display: none; 19 | } 20 | 21 | top: 0; 22 | } 23 | 24 | .platform-section2 { 25 | line-height: 34px; 26 | padding-top: 60px; 27 | 28 | @media (--lap) { 29 | padding-bottom: @padding-top; 30 | padding-top: 50px; 31 | } 32 | opacity: .4; 33 | } 34 | 35 | .platform-section { 36 | &__title { 37 | color: #000; 38 | font-size: 24px; 39 | text-align: center; 40 | opacity: .4; 41 | font-weight: normal; 42 | $hello: 20px; 43 | margin-bottom: 50px; 44 | line-height: 34px; 45 | @media (--palm) { 46 | $two: 30px; 47 | text-align: left; 48 | line-height: 24px; 49 | $one: none; 50 | margin-bottom: 18px; 51 | font-size: 18px; 52 | } 53 | } 54 | 55 | padding-top: 60px; 56 | 57 | line-height: 34px; 58 | 59 | padding-bottom: @padding-top; 60 | 61 | /* first comment */ 62 | color: #fff; 63 | 64 | margin-bottom: 50px; 65 | 66 | /* color: #000; */ 67 | 68 | font-size: 24px; 69 | 70 | text-align: center; /* second comment */ 71 | 72 | font-weight: normal; 73 | 74 | opacity: .4; 75 | 76 | @media (--lap) { 77 | padding-bottom: @padding-top; 78 | padding-top: 50px; 79 | } 80 | 81 | 82 | 83 | 84 | @media (--palm) { 85 | padding-bottom: @padding-top; 86 | padding-top: 30px; 87 | } 88 | &__title-icon { 89 | display: inline-block; 90 | margin-right: 15px; 91 | width: 34px; 92 | height: @width; 93 | vertical-align: top; 94 | 95 | @media (--palm) { 96 | width: 24px; 97 | margin-right: 10px; 98 | height: @width; 99 | } 100 | } 101 | &__block-title { 102 | margin-bottom: 10px; 103 | font-size: 36px; 104 | line-height: 46px; 105 | 106 | @media (--palm) { 107 | margin-bottom: 12px; 108 | font-weight: normal; 109 | font-size: 20px; 110 | line-height: 24px; 111 | } 112 | } 113 | &__block-description { 114 | } 115 | } 116 | .platform-intro { 117 | padding-top: 220px; 118 | padding-bottom: @padding-top; 119 | background: #3a4d53 url("img/platform/platform-intro-bg.svg") 50% 50% no-repeat; 120 | color: #fff; 121 | text-align: center; 122 | 123 | @media (--lap) { 124 | padding-top: 180px; 125 | padding-bottom: @padding-top; 126 | background-size: auto 80%; 127 | } 128 | @media (--palm) { 129 | padding-top: 90px; 130 | padding-bottom: 50px; 131 | background-image: none; 132 | } 133 | 134 | &__title { 135 | margin-bottom: 60px; 136 | font-size: 52px; 137 | line-height: 62px; 138 | 139 | @media (--lap) { 140 | margin-bottom: 50px; 141 | font-size: 36px; 142 | line-height: 50px; 143 | } 144 | @media (--palm) { 145 | margin-bottom: 36px; 146 | font-size: 24px; 147 | line-height: 30px; 148 | } 149 | } 150 | &__title-content { 151 | &--not-mobile { 152 | @media (--palm) { 153 | display: none; 154 | } 155 | } 156 | &--mobile { 157 | display: none; 158 | 159 | @media (--palm) { 160 | display: inline; 161 | } 162 | } 163 | } 164 | &__words-placeholder { 165 | span { 166 | background: #63777d !important; 167 | color: #fff !important; 168 | } 169 | } 170 | &__navigation { 171 | } 172 | } 173 | .platform-organize { 174 | @custom-media --platform-organize--lap (768px <= width <= 960px); 175 | 176 | background-image: linear-gradient(-45deg, #ff83b2 0%, #da82d8 49%, #b480ff 100%); 177 | 178 | &__row { 179 | display: flex; 180 | flex-direction: row; 181 | flex-wrap: nowrap; 182 | align-items: center; 183 | justify-content: flex-start; 184 | 185 | @media (--platform-organize--lap) { 186 | display: block; 187 | margin-bottom: 40px; 188 | text-align: center; 189 | } 190 | @media (--palm) { 191 | display: block; 192 | margin-bottom: 17px; 193 | } 194 | 195 | &:nth-child(2) { 196 | flex-direction: row-reverse; 197 | } 198 | &:nth-child(3) { 199 | margin-bottom: 50px; 200 | 201 | @media (--palm) { 202 | display: none; 203 | } 204 | } 205 | } 206 | &__text { 207 | flex: 1 1 auto; 208 | padding-bottom: 54px; /* vertical alignment because of screenshots switcher */ 209 | 210 | @media (--platform-organize--lap) { 211 | lost-center: 90%; 212 | padding-bottom: 30px; 213 | } 214 | } 215 | &__row:nth-child(2) &__text { 216 | padding-right: 40px; 217 | padding-left: 60px; 218 | 219 | @media (--lap) { 220 | padding-right: 0; 221 | } 222 | @media (--platform-organize--lap) { 223 | padding-right: 0; 224 | padding-left: 0; 225 | } 226 | @media (--palm) { 227 | margin-bottom: 15px; 228 | padding: 0; 229 | } 230 | } 231 | &__row:nth-child(3) &__text { 232 | padding-right: 80px; 233 | 234 | @media (--platform-organize--lap) { 235 | padding-right: 0; 236 | } 237 | } 238 | &__media { 239 | flex: 0 0 auto; 240 | } 241 | } 242 | .platform-navigation { 243 | @media (--palm) { 244 | lost-utility: clearfix; 245 | } 246 | 247 | &__item { 248 | position: relative; 249 | display: inline-block; 250 | width: 27%; 251 | vertical-align: middle; 252 | 253 | @media (--not-palm) { 254 | &:not(:first-child) { 255 | margin-left: -40px; 256 | } 257 | &:nth-child(1) { 258 | z-index: 3; 259 | text-indent: -15px; 260 | } 261 | &:nth-child(2) { 262 | z-index: 2; 263 | } 264 | &:nth-child(3) { 265 | z-index: 1; 266 | } 267 | &:nth-child(4) { 268 | text-indent: 15px; 269 | } 270 | } 271 | @media (--lap) { 272 | width: auto; 273 | } 274 | @media (--palm) { 275 | lost-column: 1/2 2 5px; 276 | display: block; 277 | 278 | &:nth-child(-n+2) { 279 | margin-bottom: 30px; 280 | } 281 | } 282 | } 283 | &__link { 284 | display: block; 285 | height: 80px; 286 | background-position: 100% 0; 287 | background-repeat: no-repeat; 288 | color: #fff; 289 | font-size: 22px; 290 | line-height: 80px; 291 | 292 | @media (--lap) { 293 | padding: 0 50px; 294 | } 295 | @media (--not-palm) { 296 | &:hover { 297 | color: #fff; 298 | line-height: 76px; 299 | } 300 | &--organize { 301 | background-image: inline("img/platform/platform-navigation__link--organize.svg"); 302 | } 303 | &--communicate { 304 | background-image: inline("img/platform/platform-navigation__link--communicate.svg"); 305 | } 306 | &--socialize { 307 | background-image: inline("img/platform/platform-navigation__link--socialize.svg"); 308 | } 309 | &--analyze { 310 | background: #52edc7; 311 | } 312 | } 313 | @media (--palm) { 314 | position: relative; 315 | padding-left: 40px; 316 | height: auto; 317 | text-align: left; 318 | font-size: 16px; 319 | line-height: 20px; 320 | 321 | &--organize { 322 | color: #ff83b2 !important; 323 | } 324 | &--communicate { 325 | color: #4ac0ee !important; 326 | } 327 | &--socialize { 328 | color: #fca203 !important; 329 | } 330 | &--analyze { 331 | color: #52edc7 !important; 332 | } 333 | } 334 | } 335 | &__item:first-child &__link { 336 | border-radius: 5px 0 0 5px; 337 | } 338 | &__item:last-child &__link { 339 | border-radius: 0 5px 5px 0; 340 | } 341 | &__icon { 342 | position: relative; 343 | top: -2px; 344 | margin-right: 15px; 345 | vertical-align: middle; 346 | fill: currentColor; 347 | 348 | @media (--lap) { 349 | display: none; 350 | } 351 | @media (--palm) { 352 | position: absolute; 353 | top: 50%; 354 | left: 0; 355 | transform: translate(0, -50%); 356 | } 357 | } 358 | } 359 | .platform-organize-media { 360 | &__items { 361 | margin-bottom: 24px; 362 | 363 | @media (--palm) { 364 | margin-bottom: 0; 365 | } 366 | } 367 | &__item { 368 | } 369 | &__image { 370 | border-radius: 5px; 371 | box-shadow: 0 0 13px rgba(0, 0, 0, .12); 372 | 373 | @media (--palm) { 374 | max-width: 100%; 375 | height: auto; 376 | } 377 | } 378 | &__nav { 379 | text-align: center; 380 | 381 | @media (--palm) { 382 | display: none; 383 | } 384 | } 385 | &__nav-item { 386 | display: inline-block; 387 | width: 80px; 388 | border: 2px solid #fff; 389 | font-size: 12px; 390 | line-height: 26px; 391 | cursor: pointer; 392 | transition: background .15s; 393 | 394 | &:hover { 395 | background: rgba(255, 255, 255, .2); 396 | } 397 | &:not(:first-child) { 398 | margin-left: -2px; 399 | } 400 | &:first-child { 401 | border-radius: 15px 0 0 15px; 402 | } 403 | &:last-child { 404 | border-radius: 0 15px 15px 0; 405 | } 406 | &--wide { 407 | width: 136px; 408 | } 409 | &.is-current { 410 | background: #fff; 411 | color: #de81d3; 412 | cursor: default; 413 | } 414 | } 415 | } 416 | .platform-organize-features { 417 | lost-utility: clearfix; 418 | 419 | &__item { 420 | @media (--not-palm) { 421 | lost-column: 1/4; 422 | } 423 | @media (--palm) { 424 | &:not(:last-child) { 425 | margin-bottom: 22px; 426 | } 427 | } 428 | } 429 | &__icon-wrapper { 430 | margin-bottom: 8px; 431 | height: 32px; 432 | } 433 | &__icon { 434 | } 435 | &__title { 436 | font-size: 16px; 437 | line-height: 22px; 438 | } 439 | &__description { 440 | font-size: 16px; 441 | line-height: 22px; 442 | opacity: .7; 443 | } 444 | 445 | @media (--palm) { 446 | &__item { 447 | display: flex; 448 | align-items: center; 449 | } 450 | &__icon-wrapper { 451 | min-width: 60px; 452 | } 453 | &__description { 454 | display: none; 455 | } 456 | } 457 | } 458 | .platform-communicate { 459 | background-image: linear-gradient(135deg, #01e7ff, #b480ff); 460 | 461 | &__intro { 462 | margin-bottom: 60px; 463 | text-align: center; 464 | 465 | @media (--palm) { 466 | margin-bottom: 20px; 467 | text-align: left; 468 | } 469 | } 470 | } 471 | .platform-communicate-process { 472 | @custom-media --platform-communicate-process--lap (768px <= width < 900px); 473 | @custom-media --platform-communicate-process--desk (width >= 900px); 474 | 475 | position: relative; 476 | margin: auto; 477 | counter-reset: steps; 478 | 479 | @media (--platform-communicate-process--desk) { 480 | width: calc(220px * 2 + 404px); 481 | height: calc(144px + 404px + 140px); 482 | background: url("img/platform/communicate-circle.png") 50% 144px no-repeat; 483 | background-size: 404px 403px; 484 | } 485 | 486 | &__step { 487 | position: relative; 488 | 489 | @media (--platform-communicate-process--desk) { 490 | &:nth-child(n+2) { 491 | box-sizing: content-box; 492 | max-width: 230px; 493 | } 494 | &:nth-child(1) { 495 | top: 0; 496 | left: 0; 497 | padding-bottom: 50px; 498 | width: 100%; 499 | text-align: center; 500 | 501 | &:before { 502 | bottom: 0; 503 | left: 50%; 504 | margin-left: -17px; 505 | } 506 | } 507 | &:nth-child(2), 508 | &:nth-child(3) { 509 | left: 580px; 510 | padding-left: 50px; 511 | 512 | &:before { 513 | top: 0; 514 | left: 0; 515 | } 516 | } 517 | &:nth-child(4) { 518 | top: 528px; 519 | left: 304px; 520 | padding-top: 50px; 521 | text-align: center; 522 | 523 | &:before { 524 | top: 0; 525 | left: 50%; 526 | margin-left: -17px; 527 | } 528 | } 529 | &:nth-child(5), 530 | &:nth-child(6) { 531 | right: 580px; 532 | padding-right: 50px; 533 | text-align: right; 534 | 535 | &:before { 536 | top: 0; 537 | right: 0; 538 | } 539 | } 540 | &:nth-child(2), 541 | &:nth-child(6) { 542 | top: 230px; 543 | } 544 | &:nth-child(3), 545 | &:nth-child(5) { 546 | top: 424px; 547 | } 548 | } 549 | @media (--platform-communicate-process--desk) { 550 | position: absolute; 551 | } 552 | @media (--platform-communicate-process--lap) { 553 | position: relative; 554 | padding-top: 3px; 555 | padding-left: 50px; 556 | 557 | &:not(:last-child) { 558 | margin-bottom: 20px; 559 | } 560 | } 561 | @media (--palm) { 562 | position: relative; 563 | padding-top: 7px; 564 | padding-left: 50px; 565 | height: 50px; 566 | 567 | &:last-child { 568 | height: 34px; 569 | } 570 | } 571 | 572 | &:before { 573 | position: absolute; 574 | display: block; 575 | width: 34px; 576 | height: @width; 577 | border: 1px solid #fff; 578 | border-radius: 50%; 579 | content: counter(steps, decimal); 580 | counter-increment: steps; 581 | text-align: center; 582 | font-weight: normal; 583 | line-height: 32px; 584 | 585 | @media (--platform-communicate-process--lap) { 586 | top: 0; 587 | left: 0; 588 | } 589 | @media (--palm) { 590 | top: 0; 591 | left: 0; 592 | } 593 | } 594 | } 595 | &__image-wrapper { 596 | @media (--palm) { 597 | margin-top: -15px; 598 | } 599 | } 600 | &__step:not(:first-child) &__image-wrapper { 601 | @media (--palm) { 602 | display: none; 603 | } 604 | } 605 | &__image { 606 | max-width: 100%; 607 | 608 | @media (--palm) { 609 | max-height: 56px; 610 | } 611 | } 612 | &__title { 613 | margin-bottom: 10px; 614 | font-weight: bold; 615 | font-size: 20px; 616 | line-height: 25px; 617 | 618 | @media (--palm) { 619 | margin-bottom: 0; 620 | font-weight: normal; 621 | font-size: 14px; 622 | line-height: 18px; 623 | } 624 | } 625 | } 626 | .platform-socialize { 627 | background-image: linear-gradient(135deg, #fe488b, #fca203); 628 | 629 | &__intro { 630 | margin-bottom: 47px; 631 | text-align: center; 632 | 633 | @media (--palm) { 634 | margin-bottom: 18px; 635 | text-align: left; 636 | } 637 | } 638 | &__description { 639 | @media (--desk) { 640 | lost-center: 70%; 641 | } 642 | @media (--lap) { 643 | lost-center: 90%; 644 | } 645 | } 646 | } 647 | .platform-socialize-types { 648 | lost-flex-container: row; 649 | align-items: stretch; 650 | 651 | &__list { 652 | @media (--desk) { 653 | lost-column: 5/12 flex; 654 | lost-offset: -1/12; 655 | padding-right: 30px; 656 | } 657 | @media (--lap) { 658 | lost-column: 1/2 flex; 659 | } 660 | @media (--palm) { 661 | display: none; 662 | } 663 | } 664 | &__item { 665 | margin-bottom: 2px; 666 | padding: 20px 30px; 667 | cursor: pointer; 668 | 669 | &.is-current { 670 | border-radius: 5px 0 0 5px; 671 | background: inline("img/socialize-types-item-bg.svg") 100% 0 no-repeat; 672 | background-size: auto 100%; 673 | } 674 | 675 | @media (--palm) { 676 | padding: 0; 677 | background: none !important; 678 | 679 | &:not(:last-child) { 680 | margin-bottom: 17px; 681 | } 682 | } 683 | } 684 | &__title { 685 | margin-bottom: 3px; 686 | } 687 | &__description { 688 | opacity: .7; 689 | } 690 | &__images { 691 | position: relative; 692 | 693 | @media (--desk) { 694 | lost-column: 5/12 flex; 695 | } 696 | @media (--lap) { 697 | lost-column: 1/2 flex; 698 | text-align: center; 699 | } 700 | @media (--palm) { 701 | lost-column: 2 flex; 702 | text-align: center; 703 | margin: 0 auto; 704 | height: 300px; 705 | width: 300px; 706 | margin: 0 auto !important; 707 | } 708 | } 709 | &__image-wrapper { 710 | position: absolute; 711 | top: 50%; 712 | left: 0; 713 | display: block; 714 | max-width: 100%; 715 | max-height: 100%; 716 | opacity: 1; 717 | transition: opacity .2s; 718 | transform: translateY(-50%); 719 | 720 | &.is-visible ~ & { 721 | opacity: 0; 722 | } 723 | @media (--palm) { 724 | opacity: 1 !important; 725 | } 726 | } 727 | &__image { 728 | max-width: 100%; 729 | } 730 | } 731 | .platform-analyze { 732 | background-image: linear-gradient(225deg, #52edc7, #4ac0ee); 733 | 734 | &__content { 735 | @media (--not-palm) { 736 | lost-flex-container: row; 737 | align-items: center; 738 | } 739 | } 740 | &__text { 741 | @media (--not-palm) { 742 | lost-column: 5/12 flex; 743 | lost-move: 6/12; 744 | padding-left: 30px; 745 | } 746 | @media (--lap) { 747 | lost-column: 6/12 flex; 748 | padding-left: 0; 749 | } 750 | @media (--palm) { 751 | margin-bottom: 25px; 752 | } 753 | } 754 | &__image-wrapper { 755 | @media (--not-palm) { 756 | lost-column: 6/12 flex; 757 | lost-move: -5/12; 758 | padding-right: 30px; 759 | text-align: right; 760 | } 761 | @media (--lap) { 762 | lost-move: -6/12; 763 | } 764 | } 765 | &__image { 766 | max-width: 100%; 767 | border-radius: 5px; 768 | box-shadow: 0 0 13px rgba(0, 0, 0, .12); 769 | } 770 | } 771 | .platform-features { 772 | @custom-media --platform-features-desk (width >= 1100px); 773 | @custom-media --platform-features-lap (768px <= width <= 1100px); 774 | @custom-media --platform-features-lap2 (768px <= width <= 820px); 775 | 776 | lost-utility: clearfix; 777 | 778 | &__nav { 779 | position: sticky; 780 | top: 100px; 781 | 782 | @media (--platform-features-desk) { 783 | lost-column: 3/12; 784 | } 785 | @media (--platform-features-lap) { 786 | lost-column: 4/12; 787 | } 788 | @media (--platform-features-lap2) { 789 | lost-column: 5/12; 790 | } 791 | } 792 | &__container { 793 | @media (--platform-features-desk) { 794 | lost-column: 9/12; 795 | } 796 | @media (--platform-features-lap) { 797 | lost-column: 8/12; 798 | } 799 | @media (--platform-features-lap2) { 800 | lost-column: 7/12; 801 | } 802 | } 803 | &__section { 804 | margin-bottom: calc(50px - 27px); 805 | 806 | &:first-child { 807 | padding-top: 15px; 808 | } 809 | } 810 | &__section-title { 811 | margin-bottom: 22px; 812 | color: #4ac0ee; 813 | text-transform: uppercase; 814 | letter-spacing: 2px; 815 | font-size: 14px; 816 | } 817 | &__section-icon { 818 | position: relative; 819 | top: -2px; 820 | margin-right: 6px; 821 | vertical-align: middle; 822 | 823 | @media (--palm) { 824 | display: none; 825 | } 826 | } 827 | &__list { 828 | lost-utility: clearfix; 829 | 830 | @media (--palm) { 831 | margin-top: 18px; 832 | } 833 | } 834 | &__item { 835 | margin-bottom: 27px; 836 | 837 | @media (--platform-features-desk) { 838 | lost-column: 1/3; 839 | } 840 | @media (--platform-features-lap) { 841 | lost-column: 1/2; 842 | } 843 | @media (--palm) { 844 | margin-bottom: 19px; 845 | 846 | @mixin last; 847 | } 848 | } 849 | &__feature-title { 850 | margin-bottom: 7px; 851 | 852 | @media (--palm) { 853 | margin-bottom: 3px; 854 | color: $default-color; 855 | } 856 | } 857 | &__feature-description { 858 | color: #7f8c8d; 859 | font-size: 14px; 860 | line-height: 18px; 861 | } 862 | } 863 | 864 | .early-access-button { 865 | display: none; 866 | margin-bottom: 30px; 867 | &.mobile { 868 | @media (--palm) { 869 | display: block; 870 | } 871 | } 872 | } 873 | --------------------------------------------------------------------------------