Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pure Sass support #473

Merged
merged 8 commits into from
Dec 3, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ require 'bootstrap-sass'
```

```console
compass install bootstrap
bundle exec compass install bootstrap
```

If you are creating a new Compass project, you can generate it with bootstrap-sass support:

```console
compass create my-new-project -r bootstrap-sass --using bootstrap
bundle exec compass create my-new-project -r bootstrap-sass --using bootstrap
```

This will create a new Compass project with the following files in it:
Expand All @@ -51,6 +51,50 @@ This will create a new Compass project with the following files in it:
* [styles.scss](/templates/project/styles.scss) - main project SCSS file, import `variables` and `bootstrap`.


### c. Sass-only (no Compass, nor Rails)

Require the gem, and load paths and Sass helpers will be configured automatically:

```ruby
require 'bootstrap-sass'
```

When using outside ruby (e.g. as a bower package), disable ruby asset lookup helper:

```sass
$bootstrap-sass-asset-helper: false
```


#### JS and fonts

If you are using Rails or Sprockets, see Usage.

If none of Rails/Sprockets/Compass were detected the fonts will be referenced as:

```sass
"#{$icon-font-path}/#{$icon-font-name}.eot"
```

`$icon-font-path` defaults to `bootstrap/`.

When not using an asset pipeline, you have to copy fonts and javascripts from the gem.

```bash
mkdir public/fonts
cp -r $(bundle show bootstrap-sass)/vendor/assets/fonts/ public/fonts/
mkdir public/javascripts
cp -r $(bundle show bootstrap-sass)/vendor/assets/javascripts/ public/javascripts/
```

In ruby you can get the assets' location in the filesystem like this:

```ruby
Bootstrap.stylesheets_path
Bootstrap.fonts_path
Bootstrap.javascripts_path
```

## Usage

### Sass
Expand Down Expand Up @@ -111,6 +155,8 @@ You can also load individual modules, provided you also require any dependencies
//= require bootstrap/dropdown
```

---

## Development and Contributing

If you'd like to help with the development of bootstrap-sass itself, read this section.
Expand Down
4 changes: 1 addition & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ end
desc 'Dumps output to a CSS file for testing'
task :debug do
require 'sass'
require './lib/bootstrap-sass/compass_functions'
require './lib/bootstrap-sass/sass_functions'
path = './vendor/assets/stylesheets'
path = Bootstrap.stylesheets_path
%w(bootstrap).each do |file|
engine = Sass::Engine.for_file("#{path}/#{file}.scss", syntax: :scss, load_paths: [path])
File.open("./#{file}.css", 'w') { |f| f.write(engine.render) }
Expand All @@ -29,7 +28,6 @@ task :compile, :css_path do |t, args|
lib_path = File.join(File.dirname(__FILE__), 'lib')
$:.unshift(lib_path) unless $:.include?(lib_path)
require 'sass'
require 'bootstrap-sass/compass_functions'
require 'bootstrap-sass/sass_functions'
require 'term/ansicolor'

Expand Down
53 changes: 29 additions & 24 deletions lib/bootstrap-sass.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,49 @@ class FrameworkNotFound < StandardError; end
class << self
# Inspired by Kaminari
def load!
if compass?
require 'bootstrap-sass/compass_functions'
register_compass_extension
elsif asset_pipeline?
require 'bootstrap-sass/sass_functions'
end
require 'bootstrap-sass/sass_functions'
register_compass_extension if compass?

if rails?
require 'sass-rails'
register_rails_engine
end

unless rails? || compass?
raise Bootstrap::FrameworkNotFound,
'bootstrap-sass requires either Rails > 3.1 or Compass, neither of which are loaded'
end

configure_sass
end

# Paths
def gem_path
@gem_path ||= File.expand_path '..', File.dirname(__FILE__)
end

def stylesheets_path
@stylesheets_path ||= File.join gem_path, 'vendor', 'assets', 'stylesheets'
File.join assets_path, 'stylesheets'
end

def fonts_path
File.join assets_path, 'fonts'
end

def javascripts_path
File.join assets_path, 'javascripts'
end

def assets_path
@assets_path ||= File.join gem_path, 'vendor', 'assets'
end

# Environment detection helpers
def asset_pipeline?
defined?(::Sprockets)
end

def compass?
defined?(::Compass)
end

def rails?
defined?(::Rails)
end

private
Expand All @@ -53,18 +70,6 @@ def register_compass_extension
def register_rails_engine
require 'bootstrap-sass/engine'
end

def asset_pipeline?
defined?(::Sprockets)
end

def compass?
defined?(::Compass)
end

def rails?
defined?(::Rails)
end
end
end

Expand Down
24 changes: 0 additions & 24 deletions lib/bootstrap-sass/compass_functions.rb

This file was deleted.

55 changes: 45 additions & 10 deletions lib/bootstrap-sass/sass_functions.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,49 @@
require 'sass'
require 'bootstrap-sass'

module Sass::Script::Functions
# LARS: Snatched from compass - 2011-11-29 - used for gradients in IE6-9
# returns an IE hex string for a color with an alpha channel
# suitable for passing to IE filters.
def ie_hex_str(color)
assert_type color, :Color
alpha = (color.alpha * 255).round
alphastr = alpha.to_s(16).rjust(2, '0')
Sass::Script::String.new("##{alphastr}#{color.send(:hex_str)[1..-1]}".upcase)
end
declare :ie_hex_str, [:color]
def twbs_font_path(source)
twbs_asset_path source, :font
end
declare :twbs_font_path, [:source]

def twbs_image_path(source)
twbs_asset_path source, :image
end
declare :twbs_image_path, [:source]

def twbs_asset_path(source, type)
url = if Bootstrap.asset_pipeline? && (context = sprockets_context)
context.send(:"#{type}_path", source.value)
elsif Bootstrap.compass?
send(:"#{type}_url", source, Sass::Script::Bool.new(true)).value
end

# sass-only
url ||= source.value.gsub('"', '')
Sass::Script::String.new(url, :string)
end
declare :twbs_asset_path, [:source]

unless Sass::Script::Functions.instance_methods.include?(:ie_hex_str)
# polyfill sass < 3.2.6 (taken from sass 3.2.12):
def ie_hex_str(color)
assert_type color, :Color, :color
alpha = (color.alpha * 255).round.to_s(16).rjust(2, '0')
Sass::Script::String.new("##{alpha}#{color.send(:hex_str)[1..-1]}".upcase)
end
declare :ie_hex_str, [:color]
end

protected

def sprockets_context
# Modern way to get context:
if options.key?(:sprockets)
options[:sprockets][:context]
# Compatibility with sprockets pre 2.10.0:
elsif (importer = options[:importer]) && importer.respond_to?(:context)
importer.context
end
end
end
2 changes: 1 addition & 1 deletion lib/bootstrap-sass/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Bootstrap
VERSION = '3.0.2.1'
BOOTSTRAP_SHA = '463343af63344dbbc3db04f40b0b804baa919b7e'
BOOTSTRAP_SHA = 'cc6951fa15ab990b5d90edd7acc9f1bbf2f16875'
end
28 changes: 17 additions & 11 deletions tasks/converter/less_conversion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
# This module transforms LESS into SCSS.
# It is implemented via lots of string manipulation: scanning back and forwards for regexps and doing substitions.
# Since it does not parse the LESS into an AST, bits of it may assume LESS to be formatted a certain way, and only limited,
# static analysis can be performed. This approach has so far been enough to automatically convert all of twbs/bootstrap.
# static analysis can be performed. This approach has so far been mostly enough to automatically convert most all of twbs/bootstrap.
# There is some bootstrap-specific to make up for lack of certain features in Sass 3.2 (recursion, mixin namespacing)
# and vice versa in LESS (vararg mixins).
class Converter
module LessConversion
# Some regexps for matching bits of SCSS:
Expand Down Expand Up @@ -67,6 +69,10 @@ def process_stylesheet_assets
file = replace_rules(file, ' @media') { |r| unindent(r, 2) }
when 'variables.less'
file = insert_default_vars(file)
file = <<-SCSS + file
// bootstrap specific variable. set to false if not using ruby + asset pipeline / compass.
$bootstrap-sass-asset-helper: true !default;
SCSS
file = replace_all file, /(\$icon-font-path:).*(!default)/, '\1 "bootstrap/" \2'
when 'close.less'
# extract .close { button& {...} } rule
Expand All @@ -83,9 +89,10 @@ def process_stylesheet_assets
when 'thumbnails.less'
file = extract_nested_rule file, 'a&'
when 'glyphicons.less'
file = replace_all file, /\#\{(url\(.*?\))}/, '\1'
file = replace_rules(file, '@font-face') { |rule|
rule = replace_all rule, /(\$icon-font-\w+)/, '#{\1}'
replace_all rule, /url\(/, 'font-url('
replace_asset_url rule, :font
}
end

Expand All @@ -109,14 +116,17 @@ def convert_to_scss(file)
file = replace_less_extend(file)
file = replace_spin(file)
file = replace_image_urls(file)
file = replace_image_paths(file)
file = replace_escaping(file)
file = convert_less_ampersand(file)
file = deinterpolate_vararg_mixins(file)
file = replace_calculation_semantics(file)
file
end

def replace_asset_url(rule, type)
replace_all rule, /url\((.*?)\)/, "url(if($bootstrap-sass-asset-helper, twbs-#{type}-path(\\1), \\1))"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to kick an old PR, but this if syntax is invalid Sass and blows up node-sass/libsass compilation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome that you tried this on node-sass! How does it fail exactly? Would you add a test for compiling with it?

The syntax is valid and is specified here: http://sass-lang.com/documentation/Sass/Script/Functions.html#if-instance_method
It also appears to be supported by libsass: https://github.com/hcatlin/libsass/blob/master/functions.cpp#L959-L961

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I didn't realize there was an IIF like function in Sass. The actual error is bootstrap/mixins:336: error: non-terminal statement or declaration must end with ';'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does look like the if instance is available in libsass https://github.com/hcatlin/libsass/blob/master/functions.cpp#L959

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to #494

end

# convert grid mixins LESS when => SASS @if
def convert_grid_mixins(file)
file = replace_rules file, /@mixin make-grid-columns/, comments: false do |css, pos|
Expand Down Expand Up @@ -391,15 +401,11 @@ def replace_spin(less)
end

def replace_image_urls(less)
less.gsub(/background-image: url\("?(.*?)"?\);/) { |s| "background-image: image-url(\"#{$1}\");" }
end

def replace_image_paths(less)
less.gsub('../img/', '')
less.gsub(/background-image: url\("?(.*?)"?\);/) { |s| replace_asset_url s, :image }
end

def replace_escaping(less)
less = less.gsub(/\~"([^"]+)"/, '#{\1}') # Get rid of ~"" escape
less = less.gsub(/~"([^"]+)"/, '#{\1}') # Get rid of ~"" escape
less.gsub!(/\$\{([^}]+)\}/, '$\1') # Get rid of @{} escape
less.gsub!(/"([^"\n]*)(\$[\w\-]+)([^"\n]*)"/, '"\1#{\2}\3"') # interpolate variable in string, e.g. url("$file-1x") => url("#{$file-1x}")
less.gsub(/(\W)e\(%\("?([^"]*)"?\)\)/, '\1\2') # Get rid of e(%("")) escape
Expand All @@ -415,7 +421,7 @@ def convert_less_ampersand(less)
regx = /^\.badge\s*\{[\s\/\w\(\)]+(&{1}-{1})\w.*?^}$/m

tmp = ''
less.scan(/^(\s*&)(-[\w\[\]]+\s*{.+})$/) do |ampersand, css|
less.scan(/^(\s*&)(-[\w\[\]]+\s*\{.+})$/) do |ampersand, css|
tmp << ".badge#{css}\n"
end

Expand Down Expand Up @@ -604,4 +610,4 @@ def replace_substrings_at(text, positions, replacements = nil, &block)
text
end
end
end
end
13 changes: 11 additions & 2 deletions templates/project/manifest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,22 @@
stylesheet 'styles.scss'

# SCSS:
bs_stylesheets = "../../vendor/assets/stylesheets/bootstrap"

assets = "../../vendor/assets"

bs_stylesheets = "#{assets}/stylesheets/bootstrap"
stylesheet '_variables.scss.erb', :to => '_variables.scss', :erb => true,
:bs_variables_path => File.expand_path("#{bs_stylesheets}/_variables.scss", File.dirname(__FILE__))

# JS:
bs_javascripts = "../../vendor/assets/javascripts/bootstrap"
bs_javascripts = "#{assets}/javascripts/bootstrap"
Dir.glob File.expand_path("#{bs_javascripts}/*.js", File.dirname(__FILE__)) do |path|
file = File.basename(path)
javascript "#{bs_javascripts}/#{file}", :to => "bootstrap/#{file}"
end

bs_fonts = "#{assets}/fonts/bootstrap"
Dir.glob File.expand_path("#{bs_fonts}/*", File.dirname(__FILE__)) do |path|
file = File.basename(path)
font "#{bs_fonts}/#{file}", :to => "bootstrap/#{file}"
end
Loading