diff --git a/altair/sphinxext/altairgallery.py b/altair/sphinxext/altairgallery.py
index a413bf98..6309cda2 100644
--- a/altair/sphinxext/altairgallery.py
+++ b/altair/sphinxext/altairgallery.py
@@ -34,12 +34,6 @@ This gallery contains a selection of examples of the plots Altair can create.
 
 Some may seem fairly complicated at first glance, but they are built by combining a simple set of declarative building blocks.
 
-Many draw upon sample datasets compiled by the `Vega <https://vega.github.io/vega/>`_ project. To access them yourself, install `vega_datasets <https://github.com/altair-viz/vega_datasets>`_.
-
-.. code-block::
-
-   $ pip install vega_datasets
-
 {% for grouper, group in examples %}
 
 .. _gallery-category-{{ grouper }}:
diff --git a/altair/vegalite/v1/schema/channels.py b/altair/vegalite/v1/schema/channels.py
index 046d99d9..500a36d7 100644
--- a/altair/vegalite/v1/schema/channels.py
+++ b/altair/vegalite/v1/schema/channels.py
@@ -15,6 +15,12 @@ class FieldChannelMixin(object):
         context = context or {}
         if self.shorthand is Undefined:
             kwds = {}
+        elif isinstance(self.shorthand, (tuple, list)):
+            # If given a list of shorthands, then transform it to a list of classes
+            kwds = self._kwds.copy()
+            kwds.pop('shorthand')
+            return [self.__class__(shorthand, **kwds).to_dict()
+                    for shorthand in self.shorthand]
         elif isinstance(self.shorthand, six.string_types):
             kwds = parse_shorthand(self.shorthand, data=context.get('data', None))
             type_defined = self._kwds.get('type', Undefined) is not Undefined
diff --git a/altair/vegalite/v2/api.py b/altair/vegalite/v2/api.py
index 557fe897..5a94eb4b 100644
--- a/altair/vegalite/v2/api.py
+++ b/altair/vegalite/v2/api.py
@@ -860,9 +860,14 @@ class EncodingMixin(object):
     def encode(self, *args, **kwargs):
         # First convert args to kwargs by inferring the class from the argument
         if args:
-            mapping = _get_channels_mapping()
+            channels_mapping = _get_channels_mapping()
             for arg in args:
-                encoding = mapping.get(type(arg), None)
+                if isinstance(arg, (list, tuple)) and len(arg) > 0:
+                    type_ = type(arg[0])
+                else:
+                    type_ = type(arg)
+
+                encoding = channels_mapping.get(type_, None)
                 if encoding is None:
                     raise NotImplementedError("non-keyword arg of type {0}"
                                               "".format(type(arg)))
@@ -880,6 +885,9 @@ class EncodingMixin(object):
             if isinstance(obj, six.string_types):
                 obj = {'shorthand': obj}
 
+            if isinstance(obj, (list, tuple)):
+                return [_wrap_in_channel_class(subobj, prop) for subobj in obj]
+
             if 'value' in obj:
                 clsname += 'Value'
 
diff --git a/altair/vegalite/v2/examples/simple_scatter_with_errorbars.py b/altair/vegalite/v2/examples/simple_scatter_with_errorbars.py
deleted file mode 100644
index 9118e497..00000000
--- a/altair/vegalite/v2/examples/simple_scatter_with_errorbars.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
-Simple Scatter Plot with Errorbars
-----------------------------------
-
-A simple scatter plot of a data set with errorbars.
-
-"""
-# category: scatter plots
-
-import altair as alt
-
-import numpy as np
-import pandas as pd
-
-# generate some data points with uncertainties
-np.random.seed(0)
-x = [1, 2, 3, 4, 5]
-y = np.random.normal(10, 0.5, size=len(x))
-yerr = 0.2
-
-# set up data frame
-data = pd.DataFrame({"x":x, "y":y, "yerr":yerr})
-
-# generate the points
-points = alt.Chart(data).mark_point(filled=True, size=50).encode(
-    alt.X("x", 
-          scale=alt.Scale(domain=(0,6)),
-          axis=alt.Axis(title='x')
-    ),
-    y=alt.Y('y', 
-            scale=alt.Scale(zero=False, domain=(10, 11)),
-            axis=alt.Axis(title="y")),
-    color=alt.value('black')
-)
-
-# generate the error bars
-errorbars = alt.Chart(data).mark_rule().encode(
-    x=alt.X("x"),
-    y="ymin:Q",
-    y2="ymax:Q"
-).transform_calculate(
-    ymin="datum.y-datum.yerr",
-    ymax="datum.y+datum.yerr"
-)
-
-points + errorbars
diff --git a/altair/vegalite/v2/examples/stem_and_leaf.py b/altair/vegalite/v2/examples/stem_and_leaf.py
index dd3d328d..604fe837 100644
--- a/altair/vegalite/v2/examples/stem_and_leaf.py
+++ b/altair/vegalite/v2/examples/stem_and_leaf.py
@@ -1,7 +1,7 @@
 """
-Stem and Leaf Plot
-------------------
-This example shows how to make a stem and leaf plot.
+Steam and Leaf Plot
+-------------------
+This example shows how to make a steam and leaf plot.
 """
 # category: other charts
 import altair as alt
@@ -12,7 +12,7 @@ np.random.seed(42)
 # Generating random data
 original_data = pd.DataFrame({'samples': np.array(np.random.normal(50, 15, 100), dtype=np.int)})
 
-# Splitting stem and leaf
+# Splitting steam and leaf
 original_data['stem'] = original_data['samples'].apply(lambda x: str(x)[:-1])
 original_data['leaf'] = original_data['samples'].apply(lambda x: str(x)[-1])
 
diff --git a/altair/vegalite/v2/schema/channels.py b/altair/vegalite/v2/schema/channels.py
index bc9c40e7..b780dd88 100644
--- a/altair/vegalite/v2/schema/channels.py
+++ b/altair/vegalite/v2/schema/channels.py
@@ -15,6 +15,12 @@ class FieldChannelMixin(object):
         context = context or {}
         if self.shorthand is Undefined:
             kwds = {}
+        elif isinstance(self.shorthand, (tuple, list)):
+            # If given a list of shorthands, then transform it to a list of classes
+            kwds = self._kwds.copy()
+            kwds.pop('shorthand')
+            return [self.__class__(shorthand, **kwds).to_dict()
+                    for shorthand in self.shorthand]
         elif isinstance(self.shorthand, six.string_types):
             kwds = parse_shorthand(self.shorthand, data=context.get('data', None))
             type_defined = self._kwds.get('type', Undefined) is not Undefined
diff --git a/doc/user_guide/saving_charts.rst b/doc/user_guide/saving_charts.rst
index cddaefa4..308530a6 100644
--- a/doc/user_guide/saving_charts.rst
+++ b/doc/user_guide/saving_charts.rst
@@ -74,7 +74,7 @@ For example, here we save a simple scatter-plot to JSON:
 
     chart.save('chart.json')
 
-The contents of the resulting file will look something like this:
+The contetns of the resulting file will look something like this:
 
 .. code-block:: json
 
diff --git a/setup.py b/setup.py
index 696329a4..92c40447 100644
--- a/setup.py
+++ b/setup.py
@@ -126,13 +126,12 @@ setup(name=NAME,
         'dev': DEV_REQUIRES
       },
       classifiers=[
-        'Development Status :: 5 - Production/Stable',
+        'Development Status :: 4 - Beta',
         'Environment :: Console',
         'Intended Audience :: Science/Research',
         'License :: OSI Approved :: BSD License',
         'Natural Language :: English',
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3.4',
-        'Programming Language :: Python :: 3.5',
-        'Programming Language :: Python :: 3.6'],
+        'Programming Language :: Python :: 3.5'],
      )
diff --git a/tools/generate_schema_wrapper.py b/tools/generate_schema_wrapper.py
index 0caae349..c0ad0aad 100644
--- a/tools/generate_schema_wrapper.py
+++ b/tools/generate_schema_wrapper.py
@@ -68,6 +68,12 @@ class FieldChannelMixin(object):
         context = context or {}
         if self.shorthand is Undefined:
             kwds = {}
+        elif isinstance(self.shorthand, (tuple, list)):
+            # If given a list of shorthands, then transform it to a list of classes
+            kwds = self._kwds.copy()
+            kwds.pop('shorthand')
+            return [self.__class__(shorthand, **kwds).to_dict()
+                    for shorthand in self.shorthand]
         elif isinstance(self.shorthand, six.string_types):
             kwds = parse_shorthand(self.shorthand, data=context.get('data', None))
             type_defined = self._kwds.get('type', Undefined) is not Undefined

ENH: encodings with multiple fields (e.g. tooltip)
We need to update the ``encode()`` method so that it will handle multiple encoding fields in cases where it is supported.

For example, in the most recent vega-lite release, it is possible to pass multiple fields to the tooltip encoding:
```python
from vega_datasets import data
cars = data.cars()

chart = alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color='Origin'
)

chart.encoding.tooltip = [{'field': 'Name', 'type': 'nominal'},
                          {'field': 'Origin', 'type': 'nominal'}]

chart
```
<img width="534" alt="screen shot 2018-05-08 at 10 37 35 am" src="https://user-images.githubusercontent.com/781659/39773111-e8c2f8dc-52ab-11e8-9d4b-55c7eb4f31b5.png">

Altair should make this available via a more simplified API, such as
```python
from vega_datasets import data
cars = data.cars()

chart = alt.Chart(cars).mark_point().encode(
    x='Horsepower',
    y='Miles_per_Gallon',
    color='Origin',
    tooltip=['Name', 'Origin']
)
```
diff --git a/altair/vegalite/v2/tests/test_api.py b/altair/vegalite/v2/tests/test_api.py
index 1b78f112..b78e6963 100644
--- a/altair/vegalite/v2/tests/test_api.py
+++ b/altair/vegalite/v2/tests/test_api.py
@@ -90,6 +90,30 @@ def test_chart_infer_types():
     assert dct['encoding']['y']['type'] == 'ordinal'
 
 
+def test_multiple_encodings():
+    encoding_dct = [{'field': 'value', 'type': 'quantitative'},
+                    {'field': 'name', 'type': 'nominal'}]
+    chart1 = alt.Chart('data.csv').mark_point().encode(
+        detail=['value:Q', 'name:N'],
+        tooltip=['value:Q', 'name:N']
+    )
+
+    chart2 = alt.Chart('data.csv').mark_point().encode(
+        alt.Detail(['value:Q', 'name:N']),
+        alt.Tooltip(['value:Q', 'name:N'])
+    )
+
+    chart3 = alt.Chart('data.csv').mark_point().encode(
+        [alt.Detail('value:Q'), alt.Detail('name:N')],
+        [alt.Tooltip('value:Q'), alt.Tooltip('name:N')]
+    )
+
+    for chart in [chart1, chart2, chart3]:
+        dct = chart.to_dict()
+        assert dct['encoding']['detail'] == encoding_dct
+        assert dct['encoding']['tooltip'] == encoding_dct
+
+
 def test_chart_operations():
     data = pd.DataFrame({'x': pd.date_range('2012', periods=10, freq='Y'),
                          'y': range(10),

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.16
-e git+https://github.com/altair-viz/altair.git@e6bdaf4ca09fbc83f121e7b0e835a43f665e8694#egg=altair
asttokens==3.0.0
attrs==25.3.0
babel==2.17.0
certifi==2025.1.31
charset-normalizer==3.4.1
decorator==5.2.1
docutils==0.21.2
entrypoints==0.4
exceptiongroup==1.2.2
executing==2.2.0
flake8==7.2.0
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig==2.1.0
ipython==8.18.1
jedi==0.19.2
Jinja2==3.1.6
jsonschema==4.23.0
jsonschema-specifications==2024.10.1
m2r==0.3.1
MarkupSafe==3.0.2
matplotlib-inline==0.1.7
mccabe==0.7.0
mistune==0.8.4
numpy==2.0.2
packaging==24.2
pandas==2.2.3
parso==0.8.4
pexpect==4.9.0
pluggy==1.5.0
prompt_toolkit==3.0.50
ptyprocess==0.7.0
pure_eval==0.2.3
pycodestyle==2.13.0
pyflakes==3.3.1
Pygments==2.19.1
pytest==8.3.5
python-dateutil==2.9.0.post0
pytz==2025.2
referencing==0.36.2
requests==2.32.3
rpds-py==0.24.0
six==1.17.0
snowballstemmer==2.2.0
Sphinx==7.4.7
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
stack-data==0.6.3
tomli==2.2.1
toolz==1.0.0
traitlets==5.14.3
typing==3.7.4.3
typing_extensions==4.13.0
tzdata==2025.2
urllib3==2.3.0
vega-datasets==0.9.0
wcwidth==0.2.13
zipp==3.21.0

name: altair
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.16
      - asttokens==3.0.0
      - attrs==25.3.0
      - babel==2.17.0
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - decorator==5.2.1
      - docutils==0.21.2
      - entrypoints==0.4
      - exceptiongroup==1.2.2
      - executing==2.2.0
      - flake8==7.2.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - ipython==8.18.1
      - jedi==0.19.2
      - jinja2==3.1.6
      - jsonschema==4.23.0
      - jsonschema-specifications==2024.10.1
      - m2r==0.3.1
      - markupsafe==3.0.2
      - matplotlib-inline==0.1.7
      - mccabe==0.7.0
      - mistune==0.8.4
      - numpy==2.0.2
      - packaging==24.2
      - pandas==2.2.3
      - parso==0.8.4
      - pexpect==4.9.0
      - pluggy==1.5.0
      - prompt-toolkit==3.0.50
      - ptyprocess==0.7.0
      - pure-eval==0.2.3
      - pycodestyle==2.13.0
      - pyflakes==3.3.1
      - pygments==2.19.1
      - pytest==8.3.5
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - referencing==0.36.2
      - requests==2.32.3
      - rpds-py==0.24.0
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==7.4.7
      - sphinxcontrib-applehelp==2.0.0
      - sphinxcontrib-devhelp==2.0.0
      - sphinxcontrib-htmlhelp==2.1.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==2.0.0
      - sphinxcontrib-serializinghtml==2.0.0
      - stack-data==0.6.3
      - tomli==2.2.1
      - toolz==1.0.0
      - traitlets==5.14.3
      - typing==3.7.4.3
      - typing-extensions==4.13.0
      - tzdata==2025.2
      - urllib3==2.3.0
      - vega-datasets==0.9.0
      - wcwidth==0.2.13
      - zipp==3.21.0
prefix: /opt/conda/envs/altair

altair/vegalite/v2/tests/test_api.py::test_multiple_encodings
altair/vegalite/v2/tests/test_api.py::test_chart_data_types altair/vegalite/v2/tests/test_api.py::test_chart_infer_types altair/vegalite/v2/tests/test_api.py::test_facet_parse_data altair/vegalite/v2/tests/test_api.py::test_LookupData
altair/vegalite/v2/tests/test_api.py::test_chart_operations altair/vegalite/v2/tests/test_api.py::test_selection_to_dict altair/vegalite/v2/tests/test_api.py::test_facet_parse altair/vegalite/v2/tests/test_api.py::test_SelectionMapping altair/vegalite/v2/tests/test_api.py::test_transforms altair/vegalite/v2/tests/test_api.py::test_resolve_methods altair/vegalite/v2/tests/test_api.py::test_themes altair/vegalite/v2/tests/test_api.py::test_chart_from_dict
altair/vegalite/v2/examples/simple_scatter_with_errorbars.py altair/vegalite/v2/schema/channels.py altair/vegalite/v2/examples/stem_and_leaf.py setup.py altair/vegalite/v1/schema/channels.py altair/vegalite/v2/api.py tools/generate_schema_wrapper.py altair/sphinxext/altairgallery.py doc/user_guide/saving_charts.rst
altair/vegalite/v2/examples/simple_scatter_with_errorbars.py altair/vegalite/v2/schema/channels.py altair/vegalite/v2/examples/stem_and_leaf.py setup.py altair/vegalite/v1/schema/channels.py altair/vegalite/v2/api.py tools/generate_schema_wrapper.py altair/sphinxext/altairgallery.py doc/user_guide/saving_charts.rst